/*
 * Copyright (C) 2005-2011 MaNGOS <http://www.getmangos.com/>
 *
 * Copyright (C) 2008-2011 Trinity <http://www.trinitycore.org/>
 *
 * Copyright (C) 2010-2011 ArkCORE <http://www.arkania.net/>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include "gamePCH.h"
#include "Common.h"
#include "Object.h"
#include "Language.h"
#include "DatabaseEnv.h"
#include "Log.h"
#include "Opcodes.h"
#include "SpellMgr.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "UpdateMask.h"
#include "Player.h"
#include "ClassPlayer.h"
#include "Vehicle.h"
#include "SkillDiscovery.h"
#include "QuestDef.h"
#include "GossipDef.h"
#include "UpdateData.h"
#include "Channel.h"
#include "ChannelMgr.h"
#include "MapManager.h"
#include "MapInstanced.h"
#include "InstanceSaveMgr.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "CellImpl.h"
#include "ObjectMgr.h"
#include "GuildMgr.h"
#include "ObjectAccessor.h"
#include "CreatureAI.h"
#include "Formulas.h"
#include "Group.h"
#include "Guild.h"
#include "Pet.h"
#include "Util.h"
#include "Transport.h"
#include "Weather.h"
#include "Battleground.h"
#include "BattlegroundAV.h"
#include "BattlegroundMgr.h"
#include "OutdoorPvP.h"
#include "OutdoorPvPMgr.h"
#include "ArenaTeam.h"
#include "Chat.h"
#include "Spell.h"
#include "SocialMgr.h"
#include "GameEventMgr.h"
#include "AchievementMgr.h"
#include "SpellAuras.h"
#include "SpellAuraEffects.h"
#include "ConditionMgr.h"
#include "DisableMgr.h"
#include "WeatherMgr.h"
#include "LFGMgr.h"
#include "CharacterDatabaseCleaner.h"
#include "InstanceScript.h"
#include <cmath>
#include "OutdoorPvPWG.h"

#define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS)

#define PLAYER_SKILL_INDEX(x)       (PLAYER_SKILL_INFO_1_1 + ((x)*3))
#define PLAYER_SKILL_VALUE_INDEX(x) (PLAYER_SKILL_INDEX(x)+1)
#define PLAYER_SKILL_BONUS_INDEX(x) (PLAYER_SKILL_INDEX(x)+2)

#define SKILL_VALUE(x)         PAIR32_LOPART(x)
#define SKILL_MAX(x)           PAIR32_HIPART(x)
#define MAKE_SKILL_VALUE(v, m) MAKE_PAIR32(v, m)

#define SKILL_TEMP_BONUS(x)    int16(PAIR32_LOPART(x))
#define SKILL_PERM_BONUS(x)    int16(PAIR32_HIPART(x))
#define MAKE_SKILL_BONUS(t, p) MAKE_PAIR32(t, p)

enum CharacterFlags {
	CHARACTER_FLAG_NONE = 0x00000000,
	CHARACTER_FLAG_UNK1 = 0x00000001,
	CHARACTER_FLAG_UNK2 = 0x00000002,
	CHARACTER_LOCKED_FOR_TRANSFER = 0x00000004,
	CHARACTER_FLAG_UNK4 = 0x00000008,
	CHARACTER_FLAG_UNK5 = 0x00000010,
	CHARACTER_FLAG_UNK6 = 0x00000020,
	CHARACTER_FLAG_UNK7 = 0x00000040,
	CHARACTER_FLAG_UNK8 = 0x00000080,
	CHARACTER_FLAG_UNK9 = 0x00000100,
	CHARACTER_FLAG_UNK10 = 0x00000200,
	CHARACTER_FLAG_HIDE_HELM = 0x00000400,
	CHARACTER_FLAG_HIDE_CLOAK = 0x00000800,
	CHARACTER_FLAG_UNK13 = 0x00001000,
	CHARACTER_FLAG_GHOST = 0x00002000,
	CHARACTER_FLAG_RENAME = 0x00004000,
	CHARACTER_FLAG_UNK16 = 0x00008000,
	CHARACTER_FLAG_UNK17 = 0x00010000,
	CHARACTER_FLAG_UNK18 = 0x00020000,
	CHARACTER_FLAG_UNK19 = 0x00040000,
	CHARACTER_FLAG_UNK20 = 0x00080000,
	CHARACTER_FLAG_UNK21 = 0x00100000,
	CHARACTER_FLAG_UNK22 = 0x00200000,
	CHARACTER_FLAG_UNK23 = 0x00400000,
	CHARACTER_FLAG_UNK24 = 0x00800000,
	CHARACTER_FLAG_LOCKED_BY_BILLING = 0x01000000,
	CHARACTER_FLAG_DECLINED = 0x02000000,
	CHARACTER_FLAG_UNK27 = 0x04000000,
	CHARACTER_FLAG_UNK28 = 0x08000000,
	CHARACTER_FLAG_UNK29 = 0x10000000,
	CHARACTER_FLAG_UNK30 = 0x20000000,
	CHARACTER_FLAG_UNK31 = 0x40000000,
	CHARACTER_FLAG_UNK32 = 0x80000000
};

enum CharacterCustomizeFlags {
	CHAR_CUSTOMIZE_FLAG_NONE = 0x00000000,
	CHAR_CUSTOMIZE_FLAG_CUSTOMIZE = 0x00000001, // name, gender, etc...
	CHAR_CUSTOMIZE_FLAG_FACTION = 0x00010000, // name, gender, faction, etc...
	CHAR_CUSTOMIZE_FLAG_RACE = 0x00100000
// name, gender, race, etc...
};

// corpse reclaim times
#define DEATH_EXPIRE_STEP (5*MINUTE)
#define MAX_DEATH_COUNT 3

static uint32 copseReclaimDelay[MAX_DEATH_COUNT] = { 30, 60, 120 };

// == PlayerTaxi ================================================

PlayerTaxi::PlayerTaxi() {
	// Taxi nodes
	memset(m_taximask, 0, sizeof(m_taximask));
}

void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass,
		uint8 level) {
	// class specific initial known nodes
	switch (chrClass) {
	case CLASS_DEATH_KNIGHT: {
		for (uint8 i = 0; i < TaxiMaskSize; ++i)
			m_taximask[i] |= sOldContinentsNodesMask[i];
		break;
	}
	}

	// race specific initial known nodes: capital and taxi hub masks
	switch (race) {
	case RACE_HUMAN:
		SetTaximaskNode(2);
		break; // Human
	case RACE_ORC:
		SetTaximaskNode(23);
		break; // Orc
	case RACE_DWARF:
		SetTaximaskNode(6);
		break; // Dwarf
	case RACE_NIGHTELF:
		SetTaximaskNode(26);
		SetTaximaskNode(27);
		break; // Night Elf
	case RACE_UNDEAD_PLAYER:
		SetTaximaskNode(11);
		break; // Undead
	case RACE_TAUREN:
		SetTaximaskNode(22);
		break; // Tauren
	case RACE_GNOME:
		SetTaximaskNode(6);
		break; // Gnome
	case RACE_TROLL:
		SetTaximaskNode(23);
		break; // Troll
	case RACE_BLOODELF:
		SetTaximaskNode(82);
		break; // Blood Elf
	case RACE_DRAENEI:
		SetTaximaskNode(94);
		break; // Draenei
	}

	// new continent starting masks (It will be accessible only at new map)
	switch (Player::TeamForRace(race)) {
	case ALLIANCE:
		SetTaximaskNode(100);
		break;
	case HORDE:
		SetTaximaskNode(99);
		break;
	}
	// level dependent taxi hubs
	if (level >= 68)
		SetTaximaskNode(213); //Shattered Sun Staging Area
}

void PlayerTaxi::LoadTaxiMask(const char* data) {
	Tokens tokens(data, ' ');

	uint8 index;
	Tokens::iterator iter;
	for (iter = tokens.begin(), index = 0;
			(index < TaxiMaskSize) && (iter != tokens.end()); ++iter, ++index) {
		// load and set bits only for existed taxi nodes
		m_taximask[index] = sTaxiNodesMask[index] & uint32(atol(*iter));
	}
}

void PlayerTaxi::AppendTaximaskTo(ByteBuffer& data, bool all) {
	// These uint32's are read as uint64 at client side
	if (all) {
		for (uint8 i = 0; i < TaxiMaskSize; i++)
			data << uint32(sTaxiNodesMask[i]); // all existed nodes
	} else {
		for (uint8 i = 0; i < TaxiMaskSize; i++)
			data << uint32(m_taximask[i]); // known nodes
	}
}

bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string& values,
		uint32 team) {
	ClearTaxiDestinations();

	Tokens tokens(values, ' ');

	for (Tokens::iterator iter = tokens.begin(); iter != tokens.end(); ++iter) {
		uint32 node = uint32(atol(*iter));
		AddTaxiDestination(node);
	}

	if (m_TaxiDestinations.empty())
		return true;

	// Check integrity
	if (m_TaxiDestinations.size() < 2)
		return false;

	for (size_t i = 1; i < m_TaxiDestinations.size(); ++i) {
		uint32 cost;
		uint32 path;
		sObjectMgr->GetTaxiPath(m_TaxiDestinations[i - 1],
				m_TaxiDestinations[i], path, cost);
		if (!path)
			return false;
	}

	// can't load taxi path without mount set (quest taxi path?)
	if (!sObjectMgr->GetTaxiMountDisplayId(GetTaxiSource(), team, true))
		return false;

	return true;
}

std::string PlayerTaxi::SaveTaxiDestinationsToString() {
	if (m_TaxiDestinations.empty())
		return "";

	std::ostringstream ss;

	for (size_t i = 0; i < m_TaxiDestinations.size(); ++i)
		ss << m_TaxiDestinations[i] << " ";

	return ss.str();
}

uint32 PlayerTaxi::GetCurrentTaxiPath() const {
	if (m_TaxiDestinations.size() < 2)
		return 0;

	uint32 path;
	uint32 cost;

	sObjectMgr->GetTaxiPath(m_TaxiDestinations[0], m_TaxiDestinations[1], path,
			cost);

	return path;
}

std::ostringstream& operator<<(std::ostringstream& ss, PlayerTaxi const& taxi) {
	ss << "'";
	for (uint8 i = 0; i < TaxiMaskSize; ++i)
		ss << taxi.m_taximask[i] << " ";
	ss << "'";
	return ss;
}

//== TradeData =================================================

TradeData* TradeData::GetTraderData() const {
	return m_trader->GetTradeData();
}

Item* TradeData::GetItem(TradeSlots slot) const {
	return m_items[slot] ? m_player->GetItemByGuid(m_items[slot]) : NULL;
}

bool TradeData::HasItem(uint64 item_guid) const {
	for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
		if (m_items[i] == item_guid)
			return true;

	return false;
}

Item* TradeData::GetSpellCastItem() const {
	return m_spellCastItem ? m_player->GetItemByGuid(m_spellCastItem) : NULL;
}

void TradeData::SetItem(TradeSlots slot, Item* item) {
	uint64 itemGuid = item ? item->GetGUID() : 0;

	if (m_items[slot] == itemGuid)
		return;

	m_items[slot] = itemGuid;

	SetAccepted(false);
	GetTraderData()->SetAccepted(false);

	Update();

	// need remove possible trader spell applied to changed item
	if (slot == TRADE_SLOT_NONTRADED)
		GetTraderData()->SetSpell(0);

	// need remove possible player spell applied (possible move reagent)
	SetSpell(0);
}

void TradeData::SetSpell(uint32 spell_id, Item* castItem /*= NULL*/) {
	uint64 itemGuid = castItem ? castItem->GetGUID() : 0;

	if (m_spell == spell_id && m_spellCastItem == itemGuid)
		return;

	m_spell = spell_id;
	m_spellCastItem = itemGuid;

	SetAccepted(false);
	GetTraderData()->SetAccepted(false);

	Update(true); // send spell info to item owner
	// commented out for now cause update can only be sent to the trader.
	// TODO: re-enable when proper packet/structure found
	//Update(false);                                          // send spell info to caster self
}

void TradeData::SetMoney(uint32 money) {
	if (m_money == money)
		return;

	m_money = money;

	SetAccepted(false);
	GetTraderData()->SetAccepted(false);

	Update(true);
}

void TradeData::Update(bool forTarget /*= true*/) {
	if (forTarget)
		m_trader->GetSession()->SendUpdateTrade(true); // player state for trader
	else
		m_player->GetSession()->SendUpdateTrade(false); // player state for player
}

void TradeData::SetAccepted(bool state, bool crosssend /*= false*/) {
	m_accepted = state;

	if (!state) {
		if (crosssend)
			m_trader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
		else
			m_player->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
	}
}

// == Player ====================================================

UpdateMask Player::updateVisualBits;

// we can disable this warning for this since it only
// causes undefined behavior when passed to the base class constructor
#ifdef _MSC_VER
#pragma warning(disable:4355)
#endif
Player::Player(WorldSession *session) :
		Unit(), m_achievementMgr(this), m_reputationMgr(this) {
#ifdef _MSC_VER
#pragma warning(default:4355)
#endif

	m_speakTime = 0;
	m_speakCount = 0;

	m_petSlotUsed = 0;
	m_currentPetSlot = PET_SLOT_DELETED;

	m_emote = 0;

	m_objectType |= TYPEMASK_PLAYER;
	m_objectTypeId = TYPEID_PLAYER;

	m_valuesCount = PLAYER_END;

	m_session = session;

	m_divider = 0;

	m_ExtraFlags = 0;

	m_spellModTakingSpell = NULL;
	//m_pad = 0;

	eclipse = 0;

	// players always accept
	if (GetSession()->GetSecurity() == SEC_PLAYER)
		SetAcceptWhispers(true);

	m_curSelection = 0;
	m_lootGuid = 0;

	m_comboTarget = 0;
	m_comboPoints = 0;

	m_usedTalentCount = 0;
	m_questRewardTalentCount = 0;

	m_regenTimer = 0;
	m_regenTimerCount = 0;
	m_weaponChangeTimer = 0;

	m_zoneUpdateId = 0;
	m_zoneUpdateTimer = 0;

	m_areaUpdateId = 0;

	m_nextSave = sWorld->getIntConfig(CONFIG_INTERVAL_SAVE);

	clearResurrectRequestData();

	memset(m_items, 0, sizeof(Item*) * PLAYER_SLOTS_COUNT);

	m_social = NULL;
	m_guildId = 0;

	// group is initialized in the reference constructor
	SetGroupInvite(NULL);
	m_groupUpdateMask = 0;
	m_auraRaidUpdateMask = 0;
	m_bPassOnGroupLoot = false;

	duel = NULL;

	m_GuildIdInvited = 0;
	m_ArenaTeamIdInvited = 0;

	m_atLoginFlags = AT_LOGIN_NONE;

	mSemaphoreTeleport_Near = false;
	mSemaphoreTeleport_Far = false;

	m_DelayedOperations = 0;
	m_bCanDelayTeleport = false;
	m_bHasDelayedTeleport = false;
	m_teleport_options = 0;

	m_trade = NULL;

	m_cinematic = 0;

	PlayerTalkClass = new PlayerMenu(GetSession());
	m_currentBuybackSlot = BUYBACK_SLOT_START;

	m_DailyQuestChanged = false;
	m_lastDailyQuestTime = 0;

	for (uint8 i = 0; i < MAX_TIMERS; i++)
		m_MirrorTimer[i] = DISABLED_MIRROR_TIMER;

	m_MirrorTimerFlags = UNDERWATER_NONE;
	m_MirrorTimerFlagsLast = UNDERWATER_NONE;
	m_isInWater = false;
	m_drunkTimer = 0;
	m_drunk = 0;
	m_restTime = 0;
	m_deathTimer = 0;
	m_deathExpireTime = 0;

	m_swingErrorMsg = 0;

	for (uint8 j = 0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; ++j) {
		m_bgBattlegroundQueueID[j].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE;
		m_bgBattlegroundQueueID[j].invitedToInstance = 0;
	}

	m_logintime = time(NULL);
	m_Last_tick = m_logintime;
	m_Save_Time = m_logintime + 360;
	m_WeaponProficiency = 0;
	m_ArmorProficiency = 0;
	m_canParry = false;
	m_canBlock = false;
	m_canDualWield = false;
	m_canTitanGrip = false;
	m_ammoDPS = 0.0f;

	m_temporaryUnsummonedPetNumber = 0;
	//cache for UNIT_CREATED_BY_SPELL to allow
	//returning reagents for temporarily removed pets
	//when dying/logging out
	m_oldpetspell = 0;
	m_lastpetnumber = 0;

	////////////////////Rest System/////////////////////
	time_inn_enter = 0;
	inn_pos_mapid = 0;
	inn_pos_x = 0;
	inn_pos_y = 0;
	inn_pos_z = 0;
	m_rest_bonus = 0;
	rest_type = REST_TYPE_NO;
	////////////////////Rest System/////////////////////

	m_mailsLoaded = false;
	m_mailsUpdated = false;
	unReadMails = 0;
	m_nextMailDelivereTime = 0;

	m_resetTalentsCost = 0;
	m_resetTalentsTime = 0;
	m_itemUpdateQueueBlocked = false;

	for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
		m_forced_speed_changes[i] = 0;

	/////////////////// Instance System /////////////////////

	m_HomebindTimer = 0;
	m_InstanceValid = true;
	m_dungeonDifficulty = DUNGEON_DIFFICULTY_NORMAL;
	m_raidDifficulty = RAID_DIFFICULTY_10MAN_NORMAL;

	m_lastPotionId = 0;

	m_activeSpec = 0;
	m_specsCount = 1;

	for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i) {
		for (uint8 g = 0; g < MAX_GLYPH_SLOT_INDEX; ++g)
			m_Glyphs[i][g] = 0;

		m_talents[i] = new PlayerTalentMap();

		m_branchSpec[i] = 0;
	}
	for (uint8 i = 0; i < BASEMOD_END; ++i) {
		m_auraBaseMod[i][FLAT_MOD] = 0.0f;
		m_auraBaseMod[i][PCT_MOD] = 1.0f;
	}

	for (uint8 i = 0; i < MAX_COMBAT_RATING; i++)
		m_baseRatingValue[i] = 0;

	m_spellPowerFromIntellect = 0;

	m_baseSpellPower = 0;
	m_baseFeralAP = 0;
	m_baseManaRegen = 0;
	m_baseHealthRegen = 0;
	m_spellPenetrationItemMod = 0;

	// Honor System
	m_lastHonorUpdateTime = time(NULL);
	//m_honorPoints = 0;
	//m_arenaPoints = 0;
	m_maxWeekRating[CP_SOURCE_ARENA] = 0;
	m_maxWeekRating[CP_SOURCE_RATED_BG] = 0;
	m_conquestPointsWeekCap[CP_SOURCE_ARENA] =
			PLAYER_DEFAULT_CONQUEST_POINTS_WEEK_CAP;
	m_conquestPointsWeekCap[CP_SOURCE_RATED_BG] = uint16(
			PLAYER_DEFAULT_CONQUEST_POINTS_WEEK_CAP * 1.222f); // +22, 2%

	m_IsBGRandomWinner = false;

	// Player summoning
	m_summon_expire = 0;
	m_summon_mapid = 0;
	m_summon_x = 0.0f;
	m_summon_y = 0.0f;
	m_summon_z = 0.0f;

	//Default movement to run mode
	//m_unit_movement_flags = 0;

	m_AreaID = 0;

	m_mover = this;
	m_movedPlayer = this;
	m_seer = this;

	m_contestedPvPTimer = 0;

	m_declinedname = NULL;

	m_isActive = true;

	m_runes = NULL;

	m_lastFallTime = 0;
	m_lastFallZ = 0;

	m_ControlledByPlayer = true;
	m_isWorldObject = true;

	sWorld->IncreasePlayerCount();

	m_ChampioningFaction = 0;
	m_ChampioningFactionDungeonLevel = 0;

	for (uint8 i = 0; i < MAX_POWERS; ++i)
		m_powerFraction[i] = 0;

	m_globalCooldowns.clear();

	m_ConditionErrorMsgId = 0;

	SetPendingBind(NULL, 0);
}

Player::~Player() {
	// it must be unloaded already in PlayerLogout and accessed only for loggined player
	//m_social = NULL;

	// Note: buy back item already deleted from DB when player was saved
	for (uint8 i = 0; i < PLAYER_SLOTS_COUNT; ++i)
		delete m_items[i];

	for (PlayerSpellMap::const_iterator itr = m_spells.begin();
			itr != m_spells.end(); ++itr)
		delete itr->second;

	for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i) {
		for (PlayerTalentMap::const_iterator itr = m_talents[i]->begin();
				itr != m_talents[i]->end(); ++itr)
			delete itr->second;
		delete m_talents[i];
	}

	//all mailed items should be deleted, also all mail should be deallocated
	for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
		delete *itr;

	for (ItemMap::iterator iter = mMitems.begin(); iter != mMitems.end();
			++iter)
		delete iter->second; //if item is duplicated... then server may crash ... but that item should be deallocated

	delete PlayerTalkClass;

	for (size_t x = 0; x < ItemSetEff.size(); x++)
		delete ItemSetEff[x];

	delete m_declinedname;
	delete m_runes;

	sWorld->DecreasePlayerCount();
}

void Player::CleanupsBeforeDelete(bool finalCleanup) {
	TradeCancel(false);
	DuelComplete(DUEL_INTERRUPTED);

	Unit::CleanupsBeforeDelete(finalCleanup);

	if (m_transport)
		m_transport->RemovePassenger(this);

	// clean up player-instance binds, may unload some instance saves
	for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
		for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin();
				itr != m_boundInstances[i].end(); ++itr)
			itr->second.save->RemovePlayer(this);
}

bool Player::Create(uint32 guidlow, const std::string& name, uint8 race,
		uint8 class_, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle,
		uint8 hairColor, uint8 facialHair, uint8 /*outfitId*/) {
	//FIXME: outfitId not used in player creating

	Object::_Create(guidlow, 0, HIGHGUID_PLAYER);

	ASSERT(getClass() == class_);

	m_name = name;

	PlayerInfo const* info = sObjectMgr->GetPlayerInfo(race, class_);
	if (!info) {
		sLog->outError(
				"Player have incorrect race/class pair. Can't be loaded.");
		return false;
	}

	for (uint8 i = 0; i < PLAYER_SLOTS_COUNT; i++)
		m_items[i] = NULL;

	Relocate(info->positionX, info->positionY, info->positionZ,
			info->orientation);

	ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(class_);
	if (!cEntry) {
		sLog->outError("Class %u not found in DBC (Wrong DBC files?)", class_);
		return false;
	}

	SetMap(sMapMgr->CreateMap(info->mapId, this, 0));

	uint8 powertype = cEntry->powerType;

	SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE);
	SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f);

	setFactionForRace(race);

	uint32 RaceClassGender = (race) | (class_ << 8) | (gender << 16);

	SetUInt32Value(UNIT_FIELD_BYTES_0, (RaceClassGender | (powertype << 24)));
	InitDisplayIds();
	if (sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP
			|| sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP) {
		SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP);
		SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
	}
	SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER);
	SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); // fix cast time showed in spell tooltip on client
	SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); // default for players in 3.0.3

	// -1 is default value
	SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1));

	SetUInt32Value(PLAYER_BYTES,
			(skin | (face << 8) | (hairStyle << 16) | (hairColor << 24)));
	SetUInt32Value(PLAYER_BYTES_2,
			(facialHair | (0x00 << 8) | (0x00 << 16) | (0x02 << 24)));
	SetByteValue(PLAYER_BYTES_3, 0, gender);
	SetByteValue(PLAYER_BYTES_3, 3, 0); // BattlefieldArenaFaction (0 or 1)

	SetInGuild(0);
	SetUInt32Value(PLAYER_GUILDRANK, 0);
	SetUInt32Value(PLAYER_GUILD_TIMESTAMP, 0);
	SetUInt32Value(PLAYER_GUILDDELETE_DATE, 0);
	SetUInt32Value(PLAYER_GUILDLEVEL, 1);

	for (int i = 0; i < KNOWN_TITLES_SIZE; ++i)
		SetUInt64Value(PLAYER__FIELD_KNOWN_TITLES + i, 0); // 0=disabled
	SetUInt32Value(PLAYER_CHOSEN_TITLE, 0);

	SetUInt32Value(PLAYER_FIELD_KILLS, 0);
	//SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, 0);
	//SetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, 0);
	//SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0);

	// set starting level
	uint32 start_level =
			getClass() != CLASS_DEATH_KNIGHT ?
					sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL) :
					sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL);

	if (GetSession()->GetSecurity() >= SEC_MODERATOR) {
		uint32 gm_level = sWorld->getIntConfig(CONFIG_START_GM_LEVEL);
		if (gm_level > start_level)
			start_level = gm_level;
	}

	SetUInt32Value(UNIT_FIELD_LEVEL, start_level);

	InitRunes();

	SetUInt32Value(PLAYER_FIELD_COINAGE,
			sWorld->getIntConfig(CONFIG_START_PLAYER_MONEY));
	SetCurrency(CURRENCY_TYPE_CONQUEST_POINTS,
			sWorld->getIntConfig(CONFIG_START_CONQUEST_POINTS));
	SetCurrency(CURRENCY_TYPE_HONOR_POINTS,
			sWorld->getIntConfig(CONFIG_START_HONOR_POINTS));
	SetCurrency(CURRENCY_TYPE_JUSTICE_POINTS,
			sWorld->getIntConfig(CONFIG_START_JUSTICE_POINTS));

	// start with every map explored
	if (sWorld->getBoolConfig(CONFIG_START_ALL_EXPLORED)) {
		for (uint8 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; i++)
			SetFlag(PLAYER_EXPLORED_ZONES_1 + i, 0xFFFFFFFF);
	}

	//Reputations if "StartAllReputation" is enabled, -- TODO: Fix this in a better way
	if (sWorld->getBoolConfig(CONFIG_START_ALL_REP)) {
		GetReputationMgr().SetReputation(sFactionStore.LookupEntry(942), 42999);
		GetReputationMgr().SetReputation(sFactionStore.LookupEntry(935), 42999);
		GetReputationMgr().SetReputation(sFactionStore.LookupEntry(936), 42999);
		GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1011),
				42999);
		GetReputationMgr().SetReputation(sFactionStore.LookupEntry(970), 42999);
		GetReputationMgr().SetReputation(sFactionStore.LookupEntry(967), 42999);
		GetReputationMgr().SetReputation(sFactionStore.LookupEntry(989), 42999);
		GetReputationMgr().SetReputation(sFactionStore.LookupEntry(932), 42999);
		GetReputationMgr().SetReputation(sFactionStore.LookupEntry(934), 42999);
		GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1038),
				42999);
		GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1077),
				42999);

		// Factions depending on team, like cities and some more stuff
		switch (GetTeam()) {
		case ALLIANCE:
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(72),
					42999);
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(47),
					42999);
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(69),
					42999);
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(930),
					42999);
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(730),
					42999);
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(978),
					42999);
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(54),
					42999);
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(946),
					42999);
			break;
		case HORDE:
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(76),
					42999);
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(68),
					42999);
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(81),
					42999);
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(911),
					42999);
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(729),
					42999);
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(941),
					42999);
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(530),
					42999);
			GetReputationMgr().SetReputation(sFactionStore.LookupEntry(947),
					42999);
			break;
		default:
			break;
		}
	}

	// Played time
	m_Last_tick = time(NULL);
	m_Played_time[PLAYED_TIME_TOTAL] = 0;
	m_Played_time[PLAYED_TIME_LEVEL] = 0;

	// base stats and related field values
	InitStatsForLevel();
	InitTaxiNodesForLevel();
	InitGlyphsForLevel();
	InitTalentForLevel();
	InitPrimaryProfessions(); // to max set before any spell added

	// apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
	UpdateMaxHealth(); // Update max Health (for add bonus from stamina)
	SetFullHealth();
	if (getPowerType() == POWER_MANA) {
		UpdateMaxPower(POWER_MANA); // Update max Mana (for add bonus from intellect)
		SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
	}

	//if (getPowerType() == POWER_RUNIC_POWER)
	//{
	//    SetPower(POWER_RUNE, 8);
	//    SetMaxPower(POWER_RUNE, 8);
	//    SetPower(POWER_RUNIC_POWER, 0);
	//    SetMaxPower(POWER_RUNIC_POWER, 1000);
	//}
	//if (getPowerType() == POWER_FOCUS)
	//{
	//    SetPower(POWER_FOCUS, 100);
	//    SetMaxPower(POWER_FOCUS, 100);
	//}

	if (getPowerType() != POWER_MANA) // hide additional mana bar if we have no mana
			{
		SetPower(POWER_MANA, 0);
		SetMaxPower(POWER_MANA, 0);
	}

	// original spells
	learnDefaultSpells();

	// original action bar
	for (PlayerCreateInfoActions::const_iterator action_itr =
			info->action.begin(); action_itr != info->action.end();
			++action_itr)
		addActionButton(action_itr->button, action_itr->action,
				action_itr->type);

	// original items
	CharStartOutfitEntry const* oEntry = NULL;
	for (uint32 i = 1; i < sCharStartOutfitStore.GetNumRows(); ++i) {
		if (CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(i)) {
			if (entry->RaceClassGender == RaceClassGender) {
				oEntry = entry;
				break;
			}
		}
	}

	if (oEntry) {
		for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j) {
			if (oEntry->ItemId[j] <= 0)
				continue;

			uint32 item_id = oEntry->ItemId[j];

			// just skip, reported in ObjectMgr::LoadItemPrototypes
			ItemPrototype const* iProto = ObjectMgr::GetItemPrototype(item_id);
			if (!iProto)
				continue;

			// BuyCount by default
			uint32 count = iProto->BuyCount;

			// special amount for food/drink
			if (iProto->Class == ITEM_CLASS_CONSUMABLE
					&& iProto->SubClass == ITEM_SUBCLASS_FOOD) {
				switch (iProto->Spells[0].SpellCategory) {
				case SPELL_CATEGORY_FOOD: // food
					count = getClass() == CLASS_DEATH_KNIGHT ? 10 : 4;
					break;
				case SPELL_CATEGORY_DRINK: // drink
					count = 2;
					break;
				}
				if (iProto->GetMaxStackSize() < count)
					count = iProto->GetMaxStackSize();
			}
			StoreNewItemInBestSlots(item_id, count);
		}
	}

	for (PlayerCreateInfoItems::const_iterator item_id_itr = info->item.begin();
			item_id_itr != info->item.end(); ++item_id_itr++)
		StoreNewItemInBestSlots(item_id_itr->item_id, item_id_itr->item_amount);

	// bags and main-hand weapon must equipped at this moment
	// now second pass for not equipped (offhand weapon/shield if it attempt equipped before main-hand weapon)
	// or ammo not equipped in special bag
	for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END;
			i++) {
		if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) {
			uint16 eDest;
			// equip offhand weapon/shield if it attempt equipped before main-hand weapon
			uint8 msg = CanEquipItem(NULL_SLOT, eDest, pItem, false);
			if (msg == EQUIP_ERR_OK) {
				RemoveItem(INVENTORY_SLOT_BAG_0, i, true);
				EquipItem(eDest, pItem, true);
			}
			// move other items to more appropriate slots (ammo not equipped in special bag)
			else {
				ItemPosCountVec sDest;
				msg = CanStoreItem(NULL_BAG, NULL_SLOT, sDest, pItem, false);
				if (msg == EQUIP_ERR_OK) {
					RemoveItem(INVENTORY_SLOT_BAG_0, i, true);
					pItem = StoreItem(sDest, pItem, true);
				}

				// if  this is ammo then use it
				msg = CanUseAmmo(pItem->GetEntry());
				if (msg == EQUIP_ERR_OK)
					SetAmmo(pItem->GetEntry());
			}
		}
	}
	// all item positions resolved

	return true;
}

bool Player::StoreNewItemInBestSlots(uint32 titem_id, uint32 titem_amount) {
	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
			"STORAGE: Creating initial item, itemId = %u, count = %u", titem_id,
			titem_amount);

	// attempt equip by one
	while (titem_amount > 0) {
		uint16 eDest;
		uint8 msg = CanEquipNewItem(NULL_SLOT, eDest, titem_id, false);
		if (msg != EQUIP_ERR_OK)
			break;

		EquipNewItem(eDest, titem_id, true);
		AutoUnequipOffhandIfNeed();
		--titem_amount;
	}

	if (titem_amount == 0)
		return true; // equipped

	// attempt store
	ItemPosCountVec sDest;
	// store in main bag to simplify second pass (special bags can be not equipped yet at this moment)
	uint8 msg = CanStoreNewItem(INVENTORY_SLOT_BAG_0, NULL_SLOT, sDest,
			titem_id, titem_amount);
	if (msg == EQUIP_ERR_OK) {
		StoreNewItem(sDest, titem_id, true,
				Item::GenerateItemRandomPropertyId(titem_id));
		return true; // stored
	}

	// item can't be added
	sLog->outError(
			"STORAGE: Can't equip or store initial item %u for race %u class %u , error msg = %u",
			titem_id, getRace(), getClass(), msg);
	return false;
}

void Player::SendMirrorTimer(MirrorTimerType Type, uint32 MaxValue,
		uint32 CurrentValue, int32 Regen) {
	if (int(MaxValue) == DISABLED_MIRROR_TIMER) {
		if (int(CurrentValue) != DISABLED_MIRROR_TIMER)
			StopMirrorTimer(Type);
		return;
	}
	WorldPacket data(SMSG_START_MIRROR_TIMER, (21));
	data << (uint32) Type;
	data << CurrentValue;
	data << MaxValue;
	data << Regen;
	data << (uint8) 0;
	data << (uint32) 0; // spell id
	GetSession()->SendPacket(&data);
}

void Player::StopMirrorTimer(MirrorTimerType Type) {
	m_MirrorTimer[Type] = DISABLED_MIRROR_TIMER;
	WorldPacket data(SMSG_STOP_MIRROR_TIMER, 4);
	data << (uint32) Type;
	GetSession()->SendPacket(&data);
}

uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage) {
	if (!isAlive() || isGameMaster())
		return 0;

	// Absorb, resist some environmental damage type
	uint32 absorb = 0;
	uint32 resist = 0;
	if (type == DAMAGE_LAVA)
		CalcAbsorbResist(this, SPELL_SCHOOL_MASK_FIRE, DIRECT_DAMAGE, damage,
				&absorb, &resist);
	else if (type == DAMAGE_SLIME)
		CalcAbsorbResist(this, SPELL_SCHOOL_MASK_NATURE, DIRECT_DAMAGE, damage,
				&absorb, &resist);

	damage -= absorb + resist;

	DealDamageMods(this, damage, &absorb);

	WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, (21));
	data << uint64(GetGUID());
	data << uint8(type != DAMAGE_FALL_TO_VOID ? type : DAMAGE_FALL);
	data << uint32(damage);
	data << uint32(absorb);
	data << uint32(resist);
	SendMessageToSet(&data, true);

	uint32 final_damage = DealDamage(this, damage, NULL, SELF_DAMAGE,
			SPELL_SCHOOL_MASK_NORMAL, NULL, false);

	if (!isAlive()) {
		if (type == DAMAGE_FALL) // DealDamage not apply item durability loss at self damage
				{
			sLog->outStaticDebug(
					"We are fall to death, loosing 10 percents durability");
			DurabilityLossAll(0.10f, false);
			// durability lost message
			WorldPacket data2(SMSG_DURABILITY_DAMAGE_DEATH, 0);
			GetSession()->SendPacket(&data2);
		}

		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM, 1, type);
	}

	return final_damage;
}

int32 Player::getMaxTimer(MirrorTimerType timer) {
	switch (timer) {
	case FATIGUE_TIMER:
		return MINUTE * IN_MILLISECONDS;
	case BREATH_TIMER: {
		if (!isAlive() || HasAuraType(SPELL_AURA_WATER_BREATHING)
				|| GetSession()->GetSecurity()
						>= AccountTypes(
								sWorld->getIntConfig(CONFIG_DISABLE_BREATHING)))
			return DISABLED_MIRROR_TIMER;
		int32 UnderWaterTime = 3 * MINUTE * IN_MILLISECONDS;
		AuraEffectList const& mModWaterBreathing = GetAuraEffectsByType(
				SPELL_AURA_MOD_WATER_BREATHING);
		for (AuraEffectList::const_iterator i = mModWaterBreathing.begin();
				i != mModWaterBreathing.end(); ++i)
			UnderWaterTime = uint32(
					UnderWaterTime * (100.0f + (*i)->GetAmount()) / 100.0f);
		return UnderWaterTime;
	}
	case FIRE_TIMER: {
		if (!isAlive())
			return DISABLED_MIRROR_TIMER;
		return 1 * IN_MILLISECONDS;
	}
	default:
		return 0;
	}
}

void Player::UpdateMirrorTimers() {
	// Desync flags for update on next HandleDrowning
	if (m_MirrorTimerFlags)
		m_MirrorTimerFlagsLast = ~m_MirrorTimerFlags;
}

void Player::HandleDrowning(uint32 time_diff) {
	if (!m_MirrorTimerFlags)
		return;

	// In water
	if (m_MirrorTimerFlags & UNDERWATER_INWATER) {
		if (m_zoneUpdateId == 5144)
			return;
		if (m_zoneUpdateId == 5145)
			return;
		if (m_zoneUpdateId == 4815)
			return;
		if (m_zoneUpdateId == 4816)
			return;

		// Breath timer not activated - activate it
		if (m_MirrorTimer[BREATH_TIMER] == DISABLED_MIRROR_TIMER) {
			m_MirrorTimer[BREATH_TIMER] = getMaxTimer(BREATH_TIMER);
			SendMirrorTimer(BREATH_TIMER, m_MirrorTimer[BREATH_TIMER],
					m_MirrorTimer[BREATH_TIMER], -1);
		} else // If activated - do tick
		{
			m_MirrorTimer[BREATH_TIMER] -= time_diff;
			// Timer limit - need deal damage
			if (m_MirrorTimer[BREATH_TIMER] < 0) {
				m_MirrorTimer[BREATH_TIMER] += 1 * IN_MILLISECONDS;
				// Calculate and deal damage
				// TODO: Check this formula
				uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel() - 1);
				EnvironmentalDamage(DAMAGE_DROWNING, damage);
			} else if (!(m_MirrorTimerFlagsLast & UNDERWATER_INWATER)) // Update time in client if need
				SendMirrorTimer(BREATH_TIMER, getMaxTimer(BREATH_TIMER),
						m_MirrorTimer[BREATH_TIMER], -1);
		}
	} else if (m_MirrorTimer[BREATH_TIMER] != DISABLED_MIRROR_TIMER) // Regen timer
	{
		int32 UnderWaterTime = getMaxTimer(BREATH_TIMER);
		// Need breath regen
		m_MirrorTimer[BREATH_TIMER] += 10 * time_diff;
		if (m_MirrorTimer[BREATH_TIMER] >= UnderWaterTime || !isAlive())
			StopMirrorTimer(BREATH_TIMER);
		else if (m_MirrorTimerFlagsLast & UNDERWATER_INWATER)
			SendMirrorTimer(BREATH_TIMER, UnderWaterTime,
					m_MirrorTimer[BREATH_TIMER], 10);
	}

	// In dark water
	if (m_MirrorTimerFlags & UNDERWARER_INDARKWATER) {
		if (m_zoneUpdateId == 5144)
			return;
		if (m_zoneUpdateId == 5145)
			return;
		if (m_zoneUpdateId == 4815)
			return;
		if (m_zoneUpdateId == 4816)
			return;

		// Fatigue timer not activated - activate it
		if (m_MirrorTimer[FATIGUE_TIMER] == DISABLED_MIRROR_TIMER) {
			m_MirrorTimer[FATIGUE_TIMER] = getMaxTimer(FATIGUE_TIMER);
			SendMirrorTimer(FATIGUE_TIMER, m_MirrorTimer[FATIGUE_TIMER],
					m_MirrorTimer[FATIGUE_TIMER], -1);
		} else {
			m_MirrorTimer[FATIGUE_TIMER] -= time_diff;
			// Timer limit - need deal damage or teleport ghost to graveyard
			if (m_MirrorTimer[FATIGUE_TIMER] < 0) {
				m_MirrorTimer[FATIGUE_TIMER] += 1 * IN_MILLISECONDS;
				if (isAlive()) // Calculate and deal damage
				{
					uint32 damage = GetMaxHealth() / 5
							+ urand(0, getLevel() - 1);
					EnvironmentalDamage(DAMAGE_EXHAUSTED, damage);
				} else if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) // Teleport ghost to graveyard
					RepopAtGraveyard();
			} else if (!(m_MirrorTimerFlagsLast & UNDERWARER_INDARKWATER))
				SendMirrorTimer(FATIGUE_TIMER, getMaxTimer(FATIGUE_TIMER),
						m_MirrorTimer[FATIGUE_TIMER], -1);
		}
	} else if (m_MirrorTimer[FATIGUE_TIMER] != DISABLED_MIRROR_TIMER) // Regen timer
	{
		int32 DarkWaterTime = getMaxTimer(FATIGUE_TIMER);
		m_MirrorTimer[FATIGUE_TIMER] += 10 * time_diff;
		if (m_MirrorTimer[FATIGUE_TIMER] >= DarkWaterTime || !isAlive())
			StopMirrorTimer(FATIGUE_TIMER);
		else if (m_MirrorTimerFlagsLast & UNDERWARER_INDARKWATER)
			SendMirrorTimer(FATIGUE_TIMER, DarkWaterTime,
					m_MirrorTimer[FATIGUE_TIMER], 10);
	}

	if (m_MirrorTimerFlags & (UNDERWATER_INLAVA | UNDERWATER_INSLIME)) {
		// Breath timer not activated - activate it
		if (m_MirrorTimer[FIRE_TIMER] == DISABLED_MIRROR_TIMER)
			m_MirrorTimer[FIRE_TIMER] = getMaxTimer(FIRE_TIMER);
		else {
			m_MirrorTimer[FIRE_TIMER] -= time_diff;
			if (m_MirrorTimer[FIRE_TIMER] < 0) {
				m_MirrorTimer[FIRE_TIMER] += 1 * IN_MILLISECONDS;
				// Calculate and deal damage
				// TODO: Check this formula
				uint32 damage = urand(600, 700);
				if (m_MirrorTimerFlags & UNDERWATER_INLAVA)
					EnvironmentalDamage(DAMAGE_LAVA, damage);
				// need to skip Slime damage in Undercity,
				// maybe someone can find better way to handle environmental damage
				else if (m_zoneUpdateId != 1497 && m_zoneUpdateId != 1)
					EnvironmentalDamage(DAMAGE_SLIME, damage);
			}
		}
	} else
		m_MirrorTimer[FIRE_TIMER] = DISABLED_MIRROR_TIMER;

	// Recheck timers flag
	m_MirrorTimerFlags &= ~UNDERWATER_EXIST_TIMERS;
	for (uint8 i = 0; i < MAX_TIMERS; ++i)
		if (m_MirrorTimer[i] != DISABLED_MIRROR_TIMER) {
			m_MirrorTimerFlags |= UNDERWATER_EXIST_TIMERS;
			break;
		}
	m_MirrorTimerFlagsLast = m_MirrorTimerFlags;
}

///The player sobers by 256 every 10 seconds
void Player::HandleSobering() {
	m_drunkTimer = 0;

	uint32 drunk = (m_drunk <= 256) ? 0 : (m_drunk - 256);
	SetDrunkValue(drunk);
}

DrunkenState Player::GetDrunkenstateByValue(uint16 value) {
	if (value >= 23000)
		return DRUNKEN_SMASHED;
	if (value >= 12800)
		return DRUNKEN_DRUNK;
	if (value & 0xFFFE)
		return DRUNKEN_TIPSY;
	return DRUNKEN_SOBER;
}

void Player::SetDrunkValue(uint16 newDrunkenValue, uint32 itemId) {
	uint32 oldDrunkenState = Player::GetDrunkenstateByValue(m_drunk);

	if (!newDrunkenValue && !HasAuraType(SPELL_AURA_MOD_FAKE_INEBRIATE))
		m_invisibilityDetect.AddFlag(INVISIBILITY_DRUNK);
	else
		m_invisibilityDetect.DelFlag(INVISIBILITY_DRUNK);

	m_invisibilityDetect.AddValue(INVISIBILITY_DRUNK,
			int32(newDrunkenValue - m_drunk) / 256);

	m_drunk = newDrunkenValue;
	SetUInt32Value(PLAYER_BYTES_3,
			(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFF0001) | (m_drunk & 0xFFFE));

	uint32 newDrunkenState = Player::GetDrunkenstateByValue(m_drunk);

	UpdateObjectVisibility();

	if (newDrunkenState == oldDrunkenState)
		return;

	WorldPacket data(SMSG_CROSSED_INEBRIATION_THRESHOLD, (8 + 4 + 4));
	data << uint64(GetGUID());
	data << uint32(newDrunkenState);
	data << uint32(itemId);

	SendMessageToSet(&data, true);
}

void Player::Update(uint32 p_time) {
	if (!IsInWorld())
		return;

	// undelivered mail
	if (m_nextMailDelivereTime && m_nextMailDelivereTime <= time(NULL)) {
		SendNewMail();
		++unReadMails;

		// It will be recalculate at mailbox open (for unReadMails important non-0 until mailbox open, it also will be recalculated)
		m_nextMailDelivereTime = 0;
	}

	for (std::map<uint32, uint32>::iterator itr = m_globalCooldowns.begin();
			itr != m_globalCooldowns.end(); ++itr) {
		if (itr->second) {
			if (itr->second > p_time)
				itr->second -= p_time;
			else
				itr->second = 0;
		}
	}

	// If this is set during update SetSpellModTakingSpell call is missing somewhere in the code
	// Having this would prevent more aura charges to be dropped, so let's crash
	//ASSERT (!m_spellModTakingSpell);
	if (m_spellModTakingSpell) {
		//sLog->outCrash("Player has m_pad %u during update!", m_pad);
		//if (m_spellModTakingSpell)
		sLog->outCrash("Player has m_spellModTakingSpell %u during update!",
				m_spellModTakingSpell->m_spellInfo->Id);
		m_spellModTakingSpell = NULL;
	}

	//used to implement delayed far teleports
	SetCanDelayTeleport(true);
	Unit::Update(p_time);
	SetCanDelayTeleport(false);

	time_t now = time(NULL);

	UpdatePvPFlag(now);

	UpdateContestedPvP(p_time);

	UpdateDuelFlag(now);

	CheckDuelDistance(now);

	UpdateAfkReport(now);

	if (isCharmed())
		if (Unit *charmer = GetCharmer())
			if (charmer->GetTypeId() == TYPEID_UNIT && charmer->isAlive())
				UpdateCharmedAI();

	// Update items that have just a limited lifetime
	if (now > m_Last_tick)
		UpdateItemDuration(uint32(now - m_Last_tick));

	if (now > m_Save_Time) {
		SaveToDB();
		m_Save_Time = now + 360;
	}

	// check every second
	if (now > m_Last_tick + 1)
		UpdateSoulboundTradeItems();

	if (!m_timedquests.empty()) {
		QuestSet::iterator iter = m_timedquests.begin();
		while (iter != m_timedquests.end()) {
			QuestStatusData& q_status = m_QuestStatus[*iter];
			if (q_status.m_timer <= p_time) {
				uint32 quest_id = *iter;
				++iter; // current iter will be removed in FailQuest
				FailQuest(quest_id);
			} else {
				q_status.m_timer -= p_time;
				m_QuestStatusSave[*iter] = true;
				++iter;
			}
		}
	}

	GetAchievementMgr().UpdateTimedAchievements(p_time);

	if (HasUnitState(UNIT_STAT_MELEE_ATTACKING)
			&& !HasUnitState(UNIT_STAT_CASTING)) {
		if (Unit *pVictim = getVictim()) {
			// default combat reach 10
			// TODO add weapon, skill check

			if (isAttackReady(BASE_ATTACK)) {
				if (!IsWithinMeleeRange(pVictim)) {
					setAttackTimer(BASE_ATTACK, 100);
					if (m_swingErrorMsg != 1) // send single time (client auto repeat)
							{
						SendAttackSwingNotInRange();
						m_swingErrorMsg = 1;
					}
				}
				//120 degrees of radiant range
				else if (!HasInArc(2 * M_PI / 3, pVictim)) {
					setAttackTimer(BASE_ATTACK, 100);
					if (m_swingErrorMsg != 2) // send single time (client auto repeat)
							{
						SendAttackSwingBadFacingAttack();
						m_swingErrorMsg = 2;
					}
				} else {
					m_swingErrorMsg = 0; // reset swing error state

					// prevent base and off attack in same time, delay attack at 0.2 sec
					if (haveOffhandWeapon())
						if (getAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY)
							setAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY);

					// do attack
					AttackerStateUpdate(pVictim, BASE_ATTACK);
					resetAttackTimer(BASE_ATTACK);
				}
			}

			if (haveOffhandWeapon() && isAttackReady(OFF_ATTACK)) {
				if (!IsWithinMeleeRange(pVictim))
					setAttackTimer(OFF_ATTACK, 100);
				else if (!HasInArc(2 * M_PI / 3, pVictim))
					setAttackTimer(OFF_ATTACK, 100);
				else {
					// prevent base and off attack in same time, delay attack at 0.2 sec
					if (getAttackTimer(BASE_ATTACK) < ATTACK_DISPLAY_DELAY)
						setAttackTimer(BASE_ATTACK, ATTACK_DISPLAY_DELAY);

					// do attack
					AttackerStateUpdate(pVictim, OFF_ATTACK);
					resetAttackTimer(OFF_ATTACK);
				}
			}

			/*Unit *owner = pVictim->GetOwner();
			 Unit *u = owner ? owner : pVictim;
			 if (u->IsPvP() && (!duel || duel->opponent != u))
			 {
			 UpdatePvP(true);
			 RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
			 }*/
		}
	}

	if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING)) {
		if (roll_chance_i(3) && GetTimeInnEnter() > 0) // freeze update
				{
			time_t time_inn = time(NULL) - GetTimeInnEnter();
			if (time_inn >= 10) // freeze update
					{
				float bubble = 0.125f * sWorld->getRate(RATE_REST_INGAME);
				// speed collect rest bonus (section/in hour)
				SetRestBonus(
						GetRestBonus()
								+ time_inn
										* ((float) GetUInt32Value(
												PLAYER_NEXT_LEVEL_XP) / 72000)
										* bubble);
				UpdateInnerTime(time(NULL));}
			}
		}

	if (m_weaponChangeTimer > 0) {
		if (p_time >= m_weaponChangeTimer)
			m_weaponChangeTimer = 0;
		else
			m_weaponChangeTimer -= p_time;
	}

	if (m_zoneUpdateTimer > 0) {
		if (p_time >= m_zoneUpdateTimer) {
			uint32 newzone, newarea;
			GetZoneAndAreaId(newzone, newarea);

			if (m_zoneUpdateId != newzone)
				UpdateZone(newzone, newarea); // also update area
			else {
				// use area updates as well
				// needed for free far all arenas for example
				if (m_areaUpdateId != newarea)
					UpdateArea(newarea);

				m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
			}
		} else
			m_zoneUpdateTimer -= p_time;
	}

	if (m_timeSyncTimer > 0) {
		if (p_time >= m_timeSyncTimer)
			SendTimeSync();
		else
			m_timeSyncTimer -= p_time;
	}

	if (isAlive()) {
		m_regenTimer += p_time;
		RegenerateAll();
	}

	if (m_deathState == JUST_DIED)
		KillPlayer();

	if (m_nextSave > 0) {
		if (p_time >= m_nextSave) {
			// m_nextSave reseted in SaveToDB call
			SaveToDB();
			sLog->outDetail("Player '%s' (GUID: %u) saved", GetName(),
					GetGUIDLow());
		} else
			m_nextSave -= p_time;
	}

	//Handle Water/drowning
	HandleDrowning(p_time);

	// Played time
	if (now > m_Last_tick) {
		uint32 elapsed = uint32(now - m_Last_tick);
		m_Played_time[PLAYED_TIME_TOTAL] += elapsed; // Total played time
		m_Played_time[PLAYED_TIME_LEVEL] += elapsed; // Level played time
		m_Last_tick = now;
	}

	if (m_drunk) {
		m_drunkTimer += p_time;

		if (m_drunkTimer > 10 * IN_MILLISECONDS)
			HandleSobering();
	}

	if (HasPendingBind()) {
		if (_pendingBindTimer <= p_time) {
			BindToInstance();
			SetPendingBind(NULL, 0);
		} else
			_pendingBindTimer -= p_time;
	}

	// not auto-free ghost from body in instances
	if (m_deathTimer > 0 && !GetBaseMap()->Instanceable()) {
		if (p_time >= m_deathTimer) {
			m_deathTimer = 0;
			BuildPlayerRepop();
			RepopAtGraveyard();
		} else
			m_deathTimer -= p_time;
	}

	UpdateEnchantTime(p_time);
	UpdateHomebindTime(p_time);

	if (!_instanceResetTimes.empty()) {
		for (InstanceTimeMap::iterator itr = _instanceResetTimes.begin();
				itr != _instanceResetTimes.end();) {
			if (itr->second < now)
				_instanceResetTimes.erase(itr++);
			else
				++itr;
		}
	}

	// group update
	SendUpdateToOutOfRangeGroupMembers();

	Pet* pet = GetPet();
	if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityRange())
			&& !pet->isPossessed())
		//if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityDistance()) && (GetCharmGUID() && (pet->GetGUID() != GetCharmGUID())))
		RemovePet(pet, PET_SLOT_ACTUAL_PET_SLOT, true);

	//we should execute delayed teleports only for alive(!) players
	//because we don't want player's ghost teleported from graveyard
	if (IsHasDelayedTeleport() && isAlive())
		TeleportTo(m_teleport_dest, m_teleport_options);

	if (getLevel() >= 80)
		RemoveOrAddMasterySpells();
}

void Player::setDeathState(DeathState s) {
	uint32 ressSpellId = 0;

	bool cur = isAlive();

	if (s == JUST_DIED) {
		if (!cur) {
			sLog->outError(
					"setDeathState: attempt to kill a dead player %s(%d)",
					GetName(), GetGUIDLow());
			return;
		}

		// drunken state is cleared on death
		SetDrunkValue(0);
		// lost combo points at any target (targeted combo points clear in Unit::setDeathState)
		ClearComboPoints();

		clearResurrectRequestData();

		//FIXME: is pet dismissed at dying or releasing spirit? if second, add setDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD)
		RemovePet(NULL, PET_SLOT_ACTUAL_PET_SLOT, true);

		// save value before aura remove in Unit::setDeathState
		ressSpellId = GetUInt32Value(PLAYER_SELF_RES_SPELL);

		// passive spell
		if (!ressSpellId)
			ressSpellId = GetResurrectionSpellId();
		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP, 1);
		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_DEATH, 1);
		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON, 1);
		GetAchievementMgr().ResetAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE,
				ACHIEVEMENT_CRITERIA_CONDITION_NO_DEATH);
		GetAchievementMgr().ResetAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL,
				ACHIEVEMENT_CRITERIA_CONDITION_NO_DEATH);
		GetAchievementMgr().ResetAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS,
				ACHIEVEMENT_CRITERIA_CONDITION_NO_DEATH);
	}

	Unit::setDeathState(s);

	// restore resurrection spell id for player after aura remove
	if (s == JUST_DIED && cur && ressSpellId)
		SetUInt32Value(PLAYER_SELF_RES_SPELL, ressSpellId);

	if (isAlive() && !cur)
		//clear aura case after resurrection by another way (spells will be applied before next death)
		SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
}

bool Player::BuildEnumData(QueryResult result, WorldPacket * p_data) {
	//             0               1                2                3                 4                  5                       6                        7
	//    "SELECT characters.guid, characters.name, characters.race, characters.class, characters.gender, characters.playerBytes, characters.playerBytes2, characters.level, "
	//     8                9               10                     11                     12                     13                    14
	//    "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, "
	//    15                    16                   17                     18                   19               20                     21
	//    "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.data, character_banned.guid, character_declinedname.genitive "

	Field *fields = result->Fetch();

	uint32 guid = fields[0].GetUInt32();
	uint8 pRace = fields[2].GetUInt8();
	uint8 pClass = fields[3].GetUInt8();

	PlayerInfo const *info = sObjectMgr->GetPlayerInfo(pRace, pClass);
	if (!info) {
		sLog->outError(
				"Player %u has incorrect race/class pair. Don't build enum.",
				guid);
		return false;
	}

	*p_data << uint64(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
	*p_data << fields[1].GetString(); // name
	*p_data << uint8(pRace); // race
	*p_data << uint8(pClass); // class
	*p_data << uint8(fields[4].GetUInt8()); // gender

	uint32 playerBytes = fields[5].GetUInt32();
	*p_data << uint8(playerBytes); // skin
	*p_data << uint8(playerBytes >> 8); // face
	*p_data << uint8(playerBytes >> 16); // hair style
	*p_data << uint8(playerBytes >> 24); // hair color

	uint32 playerBytes2 = fields[6].GetUInt32();
	*p_data << uint8(playerBytes2 & 0xFF); // facial hair

	*p_data << uint8(fields[7].GetUInt8()); // level
	*p_data << uint32(fields[8].GetUInt32()); // zone
	*p_data << uint32(fields[9].GetUInt32()); // map

	*p_data << fields[10].GetFloat(); // x
	*p_data << fields[11].GetFloat(); // y
	*p_data << fields[12].GetFloat(); // z

	if (uint32 guildId = fields[13].GetUInt32())
		*p_data << uint64(MAKE_NEW_GUID(guildId, 0, HIGHGUID_GUILD));
	else
		*p_data << uint64(0);

	uint32 char_flags = 0;
	uint32 playerFlags = fields[14].GetUInt32();
	uint32 atLoginFlags = fields[15].GetUInt32();
	if (playerFlags & PLAYER_FLAGS_HIDE_HELM)
		char_flags |= CHARACTER_FLAG_HIDE_HELM;
	if (playerFlags & PLAYER_FLAGS_HIDE_CLOAK)
		char_flags |= CHARACTER_FLAG_HIDE_CLOAK;
	if (playerFlags & PLAYER_FLAGS_GHOST)
		char_flags |= CHARACTER_FLAG_GHOST;
	if (atLoginFlags & AT_LOGIN_RENAME)
		char_flags |= CHARACTER_FLAG_RENAME;
	if (fields[20].GetUInt32())
		char_flags |= CHARACTER_FLAG_LOCKED_BY_BILLING;
	if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED)) {
		if (!fields[21].GetString().empty())
			char_flags |= CHARACTER_FLAG_DECLINED;
	} else
		char_flags |= CHARACTER_FLAG_DECLINED;

	*p_data << uint32(char_flags); // character flags

	// character customize flags
	if (atLoginFlags & AT_LOGIN_CUSTOMIZE)
		*p_data << uint32(CHAR_CUSTOMIZE_FLAG_CUSTOMIZE);
	else if (atLoginFlags & AT_LOGIN_CHANGE_FACTION)
		*p_data << uint32(CHAR_CUSTOMIZE_FLAG_FACTION);
	else if (atLoginFlags & AT_LOGIN_CHANGE_RACE)
		*p_data << uint32(CHAR_CUSTOMIZE_FLAG_RACE);
	else
		*p_data << uint32(CHAR_CUSTOMIZE_FLAG_NONE);

	// First login
	*p_data << uint8(atLoginFlags & AT_LOGIN_FIRST ? 1 : 0);

	// Pets info
	{
		uint32 petDisplayId = 0;
		uint32 petLevel = 0;
		uint32 petFamily = 0;

		// show pet at selection character in character list only for non-ghost character
		if (result && !(playerFlags & PLAYER_FLAGS_GHOST)
				&& (pClass == CLASS_WARLOCK || pClass == CLASS_HUNTER
						|| pClass == CLASS_DEATH_KNIGHT)) {
			uint32 entry = fields[16].GetUInt32();
			CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<
					CreatureInfo>(entry);
			if (cInfo) {
				petDisplayId = fields[17].GetUInt32();
				petLevel = fields[18].GetUInt16();
				petFamily = cInfo->family;
			}
		}

		*p_data << uint32(petDisplayId);
		*p_data << uint32(petLevel);
		*p_data << uint32(petFamily);
	}

	Tokens data(fields[19].GetString(), ' ');
	for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) {
		uint32 visualbase = slot * 2;
		uint32 item_id = GetUInt32ValueFromArray(data, visualbase);
		const ItemPrototype * proto = ObjectMgr::GetItemPrototype(item_id);
		if (!proto) {
			*p_data << uint32(0);
			*p_data << uint8(0);
			*p_data << uint32(0);
			continue;
		}

		SpellItemEnchantmentEntry const *enchant = NULL;

		uint32 enchants = GetUInt32ValueFromArray(data, visualbase + 1);
		for (uint8 enchantSlot = PERM_ENCHANTMENT_SLOT;
				enchantSlot <= TEMP_ENCHANTMENT_SLOT; ++enchantSlot) {
			// values stored in 2 uint16
			uint32 enchantId = 0x0000FFFF & (enchants >> enchantSlot * 16);
			if (!enchantId)
				continue;

			enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId);
			if (enchant)
				break;
		}

		*p_data << uint32(proto->DisplayInfoID);
		*p_data << uint8(proto->InventoryType);
		*p_data << uint32(enchant ? enchant->aura_id : 0);
	}

	*p_data << uint32(0); // bag 1 display id
	*p_data << uint8(0); // bag 1 inventory type
	*p_data << uint32(0); // enchant?
	*p_data << uint32(0); // bag 2 display id
	*p_data << uint8(0); // bag 2 inventory type
	*p_data << uint32(0); // enchant?
	*p_data << uint32(0); // bag 3 display id
	*p_data << uint8(0); // bag 3 inventory type
	*p_data << uint32(0); // enchant?
	*p_data << uint32(0); // bag 4 display id
	*p_data << uint8(0); // bag 4 inventory type
	*p_data << uint32(0); // enchant?

	return true;
}

bool Player::ToggleAFK() {
	ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK);

	bool state = HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK);

	// afk player not allowed in battleground
	if (state && InBattleground() && !InArena())
		LeaveBattleground();
	if (Guild * pGuild = sObjectMgr->GetGuildById(GetGuildId()))
		pGuild->OnPlayerStatusChange(this, GUILD_MEMBER_FLAG_AFK, state);

	return state;
}

bool Player::ToggleDND() {
	ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_DND);

	bool state = HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_DND);

	if (Guild * pGuild = sObjectMgr->GetGuildById(GetGuildId()))
		pGuild->OnPlayerStatusChange(this, GUILD_MEMBER_FLAG_DND, state);
	return state;
}

uint8 Player::chatTag() const {
	// it's bitmask
	// 0x8 - ??
	// 0x4 - gm
	// 0x2 - dnd
	// 0x1 - afk
	if (isGMChat())
		return 4;
	else if (isDND())
		return 3;
	if (isAFK())
		return 1;
	else
		return 0;
}

void Player::SendTeleportPacket(Position &oldPos) {
	WorldPacket data2(MSG_MOVE_TELEPORT, 38);
	data2.append(GetPackGUID());
	BuildMovementPacket(&data2);
	Relocate(&oldPos);
	SendMessageToSet(&data2, false);
}

void Player::SendTeleportAckPacket() {
	WorldPacket data(MSG_MOVE_TELEPORT_ACK, 41);
	data.append(GetPackGUID());
	data << uint32(0); // this value increments every time
	BuildMovementPacket(&data);
	GetSession()->SendPacket(&data);
}

// this is not used anywhere
void Player::TeleportOutOfMap(Map *oldMap) {
	while (IsBeingTeleportedFar())
		GetSession()->HandleMoveWorldportAckOpcode();

	if (GetMap() != oldMap)
		return;

	TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ,
			GetOrientation());

	while (IsBeingTeleportedFar())
		GetSession()->HandleMoveWorldportAckOpcode();

	if (GetMap() == oldMap) {
		sLog->outCrash("Cannot teleport player out of map!");
		ResetMap();
		ASSERT(false);
	}
}

bool Player::TeleportTo(uint32 mapid, float x, float y, float z,
		float orientation, uint32 options) {
	if (!MapManager::IsValidMapCoord(mapid, x, y, z, orientation)) {
		sLog->outError(
				"TeleportTo: invalid map (%d) or invalid coordinates (X: %f, Y: %f, Z: %f, O: %f) given when teleporting player (GUID: %u, name: %s, map: %d, X: %f, Y: %f, Z: %f, O: %f).",
				mapid, x, y, z, orientation, GetGUIDLow(), GetName(),
				GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(),
				GetOrientation());
		return false;
	}

	if ((GetSession()->GetSecurity() < SEC_GAMEMASTER)
			&& sDisableMgr->IsDisabledFor(DISABLE_TYPE_MAP, mapid, this)) {
		sLog->outError(
				"Player (GUID: %u, name: %s) tried to enter a forbidden map %u",
				GetGUIDLow(), GetName(), mapid);
		SendTransferAborted(mapid, TRANSFER_ABORT_MAP_NOT_ALLOWED);
		return false;
	}

	// preparing unsummon pet if lost (we must get pet before teleportation or will not find it later)
	Pet* pet = GetPet();

	MapEntry const* mEntry = sMapStore.LookupEntry(mapid);

	// don't let enter battlegrounds without assigned battleground id (for example through areatrigger)...
	// don't let gm level > 1 either
	if (!InBattleground() && mEntry->IsBattlegroundOrArena())
		return false;

	// client without expansion support
	if (GetSession()->Expansion() < mEntry->Expansion()) {
		sLog->outDebug(
				LOG_FILTER_MAPS,
				"Player %s using client without required expansion tried teleport to non accessible map %u",
				GetName(), mapid);

		if (GetTransport()) {
			m_transport->RemovePassenger(this);
			m_transport = NULL;
			m_movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f);
			m_movementInfo.t_time = 0;
			m_movementInfo.t_seat = -1;
			RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :)
		}

		SendTransferAborted(mapid, TRANSFER_ABORT_INSUF_EXPAN_LVL,
				mEntry->Expansion());

		return false; // normal client can't teleport to this map...
	} else
		sLog->outDebug(LOG_FILTER_MAPS,
				"Player %s is being teleported to map %u", GetName(), mapid);

	// reset movement flags at teleport, because player will continue move with these flags after teleport
	SetUnitMovementFlags(0);

	if (m_transport) {
		if (options & TELE_TO_NOT_LEAVE_TRANSPORT)
			AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT);
		else {
			m_transport->RemovePassenger(this);
			m_transport = NULL;
			m_movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f);
			m_movementInfo.t_time = 0;
			m_movementInfo.t_seat = -1;
		}
	}

	// The player was ported to another map and looses the duel immediately.
	// We have to perform this check before the teleport, otherwise the
	// ObjectAccessor won't find the flag.
	if (duel && GetMapId() != mapid
			&& GetMap()->GetGameObject(GetUInt64Value(PLAYER_DUEL_ARBITER)))
		DuelComplete(DUEL_FLED);

	if (GetMapId() == mapid && !m_transport
			|| (GetTransport() && GetMapId() == 628)) {
		//lets reset far teleport flag if it wasn't reset during chained teleports
		SetSemaphoreTeleportFar(false);
		//setup delayed teleport flag
		SetDelayedTeleportFlag(IsCanDelayTeleport());
		//if teleport spell is casted in Unit::Update() func
		//then we need to delay it until update process will be finished
		if (IsHasDelayedTeleport()) {
			SetSemaphoreTeleportNear(true);
			//lets save teleport destination for player
			m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
			m_teleport_options = options;
			return true;
		}

		if (!(options & TELE_TO_NOT_UNSUMMON_PET)) {
			//same map, only remove pet if out of range for new position
			if (pet
					&& !pet->IsWithinDist3d(x, y, z,
							GetMap()->GetVisibilityRange()))
				UnsummonPetTemporaryIfAny();
		}

		if (!(options & TELE_TO_NOT_LEAVE_COMBAT))
			CombatStop();

		// this will be used instead of the current location in SaveToDB
		m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
		SetFallInformation(0, z);

		// code for finish transfer called in WorldSession::HandleMovementOpcodes()
		// at client packet MSG_MOVE_TELEPORT_ACK
		SetSemaphoreTeleportNear(true);
		// near teleport, triggering send MSG_MOVE_TELEPORT_ACK from client at landing
		if (!GetSession()->PlayerLogout()) {
			Position oldPos;
			GetPosition(&oldPos);
			Relocate(x, y, z, orientation);
			SendTeleportAckPacket();
			SendTeleportPacket(oldPos); // this automatically relocates to oldPos in order to broadcast the packet in the right place
		}
	} else {
		if (getClass() == CLASS_DEATH_KNIGHT && GetMapId() == 609
				&& !isGameMaster() && !HasSpell(50977))
			return false;

		// far teleport to another map
		Map* oldmap = IsInWorld() ? GetMap() : NULL;
		// check if we can enter before stopping combat / removing pet / totems / interrupting spells

		// Check enter rights before map getting to avoid creating instance copy for player
		// this check not dependent from map instance copy and same for all instance copies of selected map
		if (!sMapMgr->CanPlayerEnter(mapid, this, false))
			return false;

		// If the map is not created, assume it is possible to enter it.
		// It will be created in the WorldPortAck.
		Map *map = sMapMgr->FindMap(mapid);
		if (!map || map->CanEnter(this)) {
			//lets reset near teleport flag if it wasn't reset during chained teleports
			SetSemaphoreTeleportNear(false);
			//setup delayed teleport flag
			SetDelayedTeleportFlag(IsCanDelayTeleport());
			//if teleport spell is casted in Unit::Update() func
			//then we need to delay it until update process will be finished
			if (IsHasDelayedTeleport()) {
				SetSemaphoreTeleportFar(true);
				//lets save teleport destination for player
				m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
				m_teleport_options = options;
				return true;
			}

			SetSelection(0);

			CombatStop();

			ResetContestedPvP();

			// remove player from battleground on far teleport (when changing maps)
			if (Battleground const* bg = GetBattleground()) {
				// Note: at battleground join battleground id set before teleport
				// and we already will found "current" battleground
				// just need check that this is targeted map or leave
				if (bg->GetMapId() != mapid)
					LeaveBattleground(false); // don't teleport to entry point
			}

			// remove arena spell coldowns/buffs now to also remove pet's cooldowns before it's temporarily unsummoned
			if (mEntry->IsBattleArena()) {
				RemoveArenaSpellCooldowns(true);
				RemoveArenaAuras();
				if (pet)
					pet->RemoveArenaAuras();
			}

			// remove pet on map change
			if (pet)
				UnsummonPetTemporaryIfAny();

			// remove all dyn objects
			RemoveAllDynObjects();

			// stop spellcasting
			// not attempt interrupt teleportation spell at caster teleport
			if (!(options & TELE_TO_SPELL))
				if (IsNonMeleeSpellCasted(true))
					InterruptNonMeleeSpells(true);

			//remove auras before removing from map...
			RemoveAurasWithInterruptFlags(
					AURA_INTERRUPT_FLAG_CHANGE_MAP | AURA_INTERRUPT_FLAG_MOVE
							| AURA_INTERRUPT_FLAG_TURNING);

			if (!GetSession()->PlayerLogout()) {
				// send transfer packets
				WorldPacket data(SMSG_TRANSFER_PENDING, (4 + 4 + 4));
				data << uint32(mapid);
				if (m_transport)
					data << m_transport->GetEntry() << GetMapId();

				GetSession()->SendPacket(&data);
			}

			// remove from old map now
			if (oldmap)
				oldmap->Remove(this, false);

			// new final coordinates
			float final_x = x;
			float final_y = y;
			float final_z = z;
			float final_o = orientation;

			if (m_transport) {
				final_x += m_movementInfo.t_pos.GetPositionX();
				final_y += m_movementInfo.t_pos.GetPositionY();
				final_z += m_movementInfo.t_pos.GetPositionZ();
				final_o += m_movementInfo.t_pos.GetOrientation();
			}

			m_teleport_dest = WorldLocation(mapid, final_x, final_y, final_z,
					final_o);
			SetFallInformation(0, final_z);
			// if the player is saved before worldportack (at logout for example)
			// this will be used instead of the current location in SaveToDB

			if (!GetSession()->PlayerLogout()) {
				WorldPacket data(SMSG_NEW_WORLD, 4 + 4 + 4 + 4 + 4);
				data << uint32(mapid);
				if (m_transport)
					data << m_movementInfo.t_pos.PositionXYZOStream();
				else
					data << m_teleport_dest.PositionXYZOStream();

				GetSession()->SendPacket(&data);
				SendSavedInstances();
			}

			// move packet sent by client always after far teleport
			// code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet
			SetSemaphoreTeleportFar(true);
		} else
			return false;
	}
	return true;
}

bool Player::TeleportToBGEntryPoint() {
	ScheduleDelayedOperation(DELAYED_BG_MOUNT_RESTORE);
	ScheduleDelayedOperation(DELAYED_BG_TAXI_RESTORE);
	return TeleportTo(m_bgData.joinPos);
}

void Player::ProcessDelayedOperations() {
	if (m_DelayedOperations == 0)
		return;

	if (m_DelayedOperations & DELAYED_RESURRECT_PLAYER) {
		ResurrectPlayer(0.0f, false);

		if (GetMaxHealth() > m_resurrectHealth)
			SetHealth(m_resurrectHealth);
		else
			SetFullHealth();

		if (GetMaxPower(POWER_MANA) > m_resurrectMana)
			SetPower(POWER_MANA, m_resurrectMana);
		else
			SetPower(POWER_MANA, GetMaxPower(POWER_MANA));

		SetPower(POWER_RAGE, 0);
		SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));

		SpawnCorpseBones();
	}

	if (m_DelayedOperations & DELAYED_SAVE_PLAYER)
		SaveToDB();

	if (m_DelayedOperations & DELAYED_SPELL_CAST_DESERTER)
		CastSpell(this, 26013, true); // Deserter

	if (m_DelayedOperations & DELAYED_BG_MOUNT_RESTORE) {
		if (m_bgData.mountSpell) {
			CastSpell(this, m_bgData.mountSpell, true);
			m_bgData.mountSpell = 0;
		}
	}

	if (m_DelayedOperations & DELAYED_BG_TAXI_RESTORE) {
		if (m_bgData.HasTaxiPath()) {
			m_taxi.AddTaxiDestination(m_bgData.taxiPath[0]);
			m_taxi.AddTaxiDestination(m_bgData.taxiPath[1]);
			m_bgData.ClearTaxiPath();

			ContinueTaxiFlight();
		}
	}

	//we have executed ALL delayed ops, so clear the flag
	m_DelayedOperations = 0;
}

void Player::AddToWorld() {
	///- Do not add/remove the player from the object storage
	///- It will crash when updating the ObjectAccessor
	///- The player should only be added when logging in
	Unit::AddToWorld();

	for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i)
		if (m_items[i])
			m_items[i]->AddToWorld();
}

void Player::RemoveFromWorld() {
	// cleanup
	if (IsInWorld()) {
		///- Release charmed creatures, unsummon totems and remove pets/guardians
		StopCastingCharm();
		StopCastingBindSight();
		UnsummonPetTemporaryIfAny();
		sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
	}

	///- Do not add/remove the player from the object storage
	///- It will crash when updating the ObjectAccessor
	///- The player should only be removed when logging out
	Unit::RemoveFromWorld();

	for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i) {
		if (m_items[i])
			m_items[i]->RemoveFromWorld();
	}

	for (ItemMap::iterator iter = mMitems.begin(); iter != mMitems.end();
			++iter)
		iter->second->RemoveFromWorld();

	if (m_uint32Values) {
		if (WorldObject *viewpoint = GetViewpoint()) {
			sLog->outCrash(
					"Player %s has viewpoint %u %u when removed from world",
					GetName(), viewpoint->GetEntry(), viewpoint->GetTypeId());
			SetViewpoint(viewpoint, false);
		}
	}
}

void Player::RegenerateAll() {
	//if (m_regenTimer <= 500)
	//    return;

	m_regenTimerCount += m_regenTimer;
	if (getClass() == CLASS_PALADIN)
		m_holyPowerRegenTimerCount += m_regenTimer;

	Regenerate(POWER_ENERGY);

	Regenerate(POWER_MANA);

	// Runes act as cooldowns, and they don't need to send any data
	if (getClass() == CLASS_DEATH_KNIGHT)
		for (uint32 i = 0; i < MAX_RUNES; i += 2) {
			uint32 cd1 = GetRuneCooldown(i);
			uint32 cd2 = GetRuneCooldown(i + 1);

			if (cd1 && (!cd2 || cd1 <= cd2))
				SetRuneCooldown(i,
						(cd1 > m_regenTimer) ? cd1 - m_regenTimer : 0);
			else if (cd2)
				SetRuneCooldown(i + 1,
						(cd2 > m_regenTimer) ? cd2 - m_regenTimer : 0);
		}

	if (m_regenTimerCount >= 2000) {
		// Not in combat or they have regeneration
		if (!isInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT)
				|| HasAuraType(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT)
				|| IsPolymorphed()) {
			RegenerateHealth();
		}

		Regenerate(POWER_RAGE);
        if (getClass() == CLASS_PALADIN)
            Regenerate(POWER_HOLY_POWER);
        if (getClass() == CLASS_DEATH_KNIGHT)
            Regenerate(POWER_RUNIC_POWER);
        if (getClass() == CLASS_HUNTER)
            Regenerate(POWER_FOCUS);

		m_regenTimerCount -= 2000;
	}

	if (m_holyPowerRegenTimerCount >= 10000 && getClass() == CLASS_PALADIN) {
		Regenerate(POWER_HOLY_POWER);
		m_holyPowerRegenTimerCount -= 10000;
	}

	m_regenTimer = 0;
}

void Player::Regenerate(Powers power) {
	uint32 maxValue = GetMaxPower(power);
	if (!maxValue)
		return;

	uint32 curValue = GetPower(power);

	// TODO: possible use of miscvalueb instead of amount
	if (HasAuraTypeWithValue(SPELL_AURA_PREVENT_REGENERATE_POWER, power))
		return;

	float addvalue = 0.0f;

	//powers now benefit from haste.
	float haste = (2 - GetFloatValue(UNIT_MOD_CAST_SPEED));

	switch (power) {
	case POWER_MANA: {
		float ManaIncreaseRate = sWorld->getRate(RATE_POWER_MANA);

		if (getLevel() < 15)
			ManaIncreaseRate = sWorld->getRate(RATE_POWER_MANA)
					* (2.066f - (getLevel() * 0.066f));

		if (isInCombat()) // Trinity updates Mana in intervals of 2s, which is correct
			addvalue += GetFloatValue(
					UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER)
					* ManaIncreaseRate * 0.001f * m_regenTimer * haste;
		else
			addvalue += GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER)
					* ManaIncreaseRate * 0.001f * m_regenTimer * haste;
	}
		break;
	case POWER_RAGE: // Regenerate rage
	{
		if (!isInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN)) {
			float RageDecreaseRate = sWorld->getRate(RATE_POWER_RAGE_LOSS);
			addvalue += -20 * RageDecreaseRate / haste; // 2 rage by tick (= 2 seconds => 1 rage/sec)
		}
	}
		break;
	case POWER_FOCUS:
		addvalue = 12 * haste;
		break;
	case POWER_HOLY_POWER: {
		if (!isInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN)) {
			addvalue += -1;
		}
		break;
	}
	case POWER_ENERGY: // Regenerate energy (rogue)
		addvalue += 0.01f * m_regenTimer * haste
				* sWorld->getRate(RATE_POWER_ENERGY);
		break;
	case POWER_RUNIC_POWER: {
		if (!isInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN)) {
			float RunicPowerDecreaseRate = sWorld->getRate(
					RATE_POWER_RUNICPOWER_LOSS);
			addvalue += -30 * RunicPowerDecreaseRate; // 3 RunicPower by tick
		}
	}
		break;
	case POWER_RUNE:
	case POWER_HAPPINESS:
	case POWER_HEALTH:
		break;
	default:
		break;
	}

	// Mana regen calculated in Player::UpdateManaRegen()
	if (power != POWER_MANA) {
		AuraEffectList const& ModPowerRegenPCTAuras = GetAuraEffectsByType(
				SPELL_AURA_MOD_POWER_REGEN_PERCENT);
		for (AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin();
				i != ModPowerRegenPCTAuras.end(); ++i)
			if (Powers((*i)->GetMiscValue()) == power)
				addvalue *= ((*i)->GetAmount() + 100) / 100.0f;

		// Butchery requires combat for this effect
		if (power != POWER_RUNIC_POWER || isInCombat())
			addvalue += GetTotalAuraModifierByMiscValue(
					SPELL_AURA_MOD_POWER_REGEN, power)
					* ((power != POWER_ENERGY) ?
							m_regenTimerCount : m_regenTimer)
					/ (5 * IN_MILLISECONDS);
	}

	if (addvalue < 0.0f) {
		if (curValue == 0)
			return;
	} else if (addvalue > 0.0f) {
		if (curValue == maxValue)
			return;
	} else
		return;

	addvalue += m_powerFraction[power];
	uint32 integerValue = uint32(fabs(addvalue));

	if (addvalue < 0.0f) {
		if (curValue > integerValue) {
			curValue -= integerValue;
			m_powerFraction[power] = addvalue + integerValue;
		} else {
			curValue = 0;
			m_powerFraction[power] = 0;
		}
	} else {
		curValue += integerValue;

		if (curValue > maxValue) {
			curValue = maxValue;
			m_powerFraction[power] = 0;
		} else
			m_powerFraction[power] = addvalue - integerValue;
	}
	if (m_regenTimerCount >= 2000 || m_holyPowerRegenTimerCount >= 10000)
		SetPower(power, curValue);
	else
		UpdateUInt32Value(UNIT_FIELD_POWER1 + power, curValue);
}

void Player::RegenerateHealth() {
	uint32 curValue = GetHealth();
	uint32 maxValue = GetMaxHealth();

	if (curValue >= maxValue)
		return;

	float HealthIncreaseRate = sWorld->getRate(RATE_HEALTH);

	if (getLevel() < 15)
		HealthIncreaseRate = sWorld->getRate(RATE_HEALTH)
				* (2.066f - (getLevel() * 0.066f));

	float addvalue = 0.0f;

	// polymorphed case
	if (IsPolymorphed())
		addvalue = (float) GetMaxHealth() / 3;
	// normal regen case (maybe partly in combat case)
	else if (!isInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT)) {
		addvalue = 0.015f * ((float) GetMaxHealth()) * HealthIncreaseRate; //2 secs: 0.75*2 :-) source: wowpedia.
		if (!isInCombat()) {
			AuraEffectList const& mModHealthRegenPct = GetAuraEffectsByType(
					SPELL_AURA_MOD_HEALTH_REGEN_PERCENT);
			for (AuraEffectList::const_iterator i = mModHealthRegenPct.begin();
					i != mModHealthRegenPct.end(); ++i)
				addvalue *= (100.0f + (*i)->GetAmount()) / 100.0f;

			addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_REGEN) * 2
					* IN_MILLISECONDS / (5 * IN_MILLISECONDS);
		} else if (HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT))
			addvalue *= GetTotalAuraModifier(SPELL_AURA_MOD_REGEN_DURING_COMBAT)
					/ 100.0f;

		if (!IsStandState())
			addvalue *= 1.5;
	}

	// always regeneration bonus (including combat)
	addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT);
	addvalue += m_baseHealthRegen / 2.5f;

	if (addvalue < 0)
		addvalue = 0;

	ModifyHealth(int32(addvalue));
}

void Player::ResetAllPowers() {
	SetHealth(GetMaxHealth());
	switch (getPowerType()) {
	case POWER_MANA:
		SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
		break;
	case POWER_RAGE:
		SetPower(POWER_RAGE, 0);
		break;
	case POWER_ENERGY:
		SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
		break;
	case POWER_RUNIC_POWER:
		SetPower(POWER_RUNIC_POWER, 0);
		break;
	default:
		break;
	}
}

Creature* Player::GetNPCIfCanInteractWith(uint64 guid, uint32 npcflagmask) {
	// unit checks
	if (!guid)
		return NULL;

	if (!IsInWorld())
		return NULL;

	if (isInFlight())
		return NULL;

	// exist (we need look pets also for some interaction (quest/etc)
	Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, guid);
	if (!creature)
		return NULL;

	// Deathstate checks
	if (!isAlive()
			&& !(creature->GetCreatureInfo()->type_flags
					& CREATURE_TYPEFLAGS_GHOST))
		return NULL;

	// alive or spirit healer
	if (!creature->isAlive()
			&& !(creature->GetCreatureInfo()->type_flags
					& CREATURE_TYPEFLAGS_DEAD_INTERACT))
		return NULL;

	// appropriate npc type
	if (npcflagmask && !creature->HasFlag(UNIT_NPC_FLAGS, npcflagmask))
		return NULL;

	// not allow interaction under control, but allow with own pets
	if (creature->GetCharmerGUID())
		return NULL;

	// not enemy
	if (creature->IsHostileTo(this))
		return NULL;

	// not unfriendly
	if (FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(creature->getFaction()))
		if (factionTemplate->faction)
			if (FactionEntry const* faction = sFactionStore.LookupEntry(factionTemplate->faction))
				if (faction->reputationListID >= 0
						&& GetReputationMgr().GetRank(faction) <= REP_UNFRIENDLY)
					return NULL;

	// not too far
	if (!creature->IsWithinDistInMap(this, INTERACTION_DISTANCE))
		return NULL;

	return creature;
}

GameObject* Player::GetGameObjectIfCanInteractWith(uint64 guid,
		GameobjectTypes type) const {
	if (GameObject *go = GetMap()->GetGameObject(guid)) {
		if (go->GetGoType() == type) {
			float maxdist;
			switch (type) {
			// TODO: find out how the client calculates the maximal usage distance to spellless working
			// gameobjects like guildbanks and mailboxes - 10.0 is a just an abitrary choosen number
			case GAMEOBJECT_TYPE_GUILD_BANK:
			case GAMEOBJECT_TYPE_MAILBOX:
				maxdist = 10.0f;
				break;
			case GAMEOBJECT_TYPE_FISHINGHOLE:
				maxdist = 20.0f + CONTACT_DISTANCE; // max spell range
				break;
			default:
				maxdist = INTERACTION_DISTANCE;
				break;
			}

			if (go->IsWithinDistInMap(this, maxdist))
				return go;

			sLog->outDebug(
					LOG_FILTER_MAPS,
					"IsGameObjectOfTypeInRange: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal 10 is allowed)",
					go->GetGOInfo()->name, go->GetGUIDLow(), GetName(),
					GetGUIDLow(), go->GetDistance(this));
		}
	}
	return NULL;
}

bool Player::IsUnderWater() const {
	return IsInWater()
			&& GetPositionZ()
					< (GetBaseMap()->GetWaterLevel(GetPositionX(),
							GetPositionY()) - 2);
}

void Player::SetInWater(bool apply) {
	if (m_isInWater == apply)
		return;

	//define player in water by opcodes
	//move player's guid into HateOfflineList of those mobs
	//which can't swim and move guid back into ThreatList when
	//on surface.
	//TODO: exist also swimming mobs, and function must be symmetric to enter/leave water
	m_isInWater = apply;

	// remove auras that need water/land
	RemoveAurasWithInterruptFlags(
			apply ? AURA_INTERRUPT_FLAG_NOT_ABOVEWATER : AURA_INTERRUPT_FLAG_NOT_UNDERWATER);

	getHostileRefManager().updateThreatTables();
}

void Player::SetGameMaster(bool on) {
	if (on) {
		m_ExtraFlags |= PLAYER_EXTRA_GM_ON;
		setFaction(35);

		if (sWorld->getIntConfig(CONFIG_GM_DEV_TAG_ENABLE) == 1) {
			if (sWorld->getIntConfig(CONFIG_GM_DEV_TAG_LEVEL)
					== GetSession()->GetSecurity())
				SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_DEVELOPER);
			else
				SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
		} else
			SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);

		if (Pet* pet = GetPet()) {
			pet->setFaction(35);
			pet->getHostileRefManager().setOnlineOfflineState(false);
		}

		RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
		ResetContestedPvP();

		getHostileRefManager().setOnlineOfflineState(false);
		CombatStopWithPets();

		SetPhaseMask(PHASEMASK_ANYWHERE, false); // see and visible in all phases
		m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GM,
				GetSession()->GetSecurity());
	} else {
		// restore phase
		uint32 newPhase = 0;
		AuraEffectList const& phases = GetAuraEffectsByType(SPELL_AURA_PHASE);
		if (!phases.empty())
			for (AuraEffectList::const_iterator itr = phases.begin();
					itr != phases.end(); ++itr)
				newPhase |= (*itr)->GetMiscValue();

		if (!newPhase)
			newPhase = PHASEMASK_NORMAL;

		m_ExtraFlags &= ~PLAYER_EXTRA_GM_ON;
		setFactionForRace(getRace());
		RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
		RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_DEVELOPER);

		if (Pet* pet = GetPet()) {
			pet->setFaction(getFaction());
			pet->getHostileRefManager().setOnlineOfflineState(true);
		}

		// restore FFA PvP Server state
		if (sWorld->IsFFAPvPRealm())
			SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);

		// restore FFA PvP area state, remove not allowed for GM mounts
		UpdateArea(m_areaUpdateId);

		getHostileRefManager().setOnlineOfflineState(true);
		m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GM,
				SEC_PLAYER);
	}

	UpdateObjectVisibility();
}

void Player::SetGMVisible(bool on) {
	if (on) {
		m_ExtraFlags &= ~PLAYER_EXTRA_GM_INVISIBLE; //remove flag

		m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER);
	} else {
		m_ExtraFlags |= PLAYER_EXTRA_GM_INVISIBLE; //add flag

		SetAcceptWhispers(false);
		SetGameMaster(true);

		m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM,
				GetSession()->GetSecurity());
	}
}

bool Player::IsGroupVisibleFor(Player const* p) const {
	switch (sWorld->getIntConfig(CONFIG_GROUP_VISIBILITY)) {
	default:
		return IsInSameGroupWith(p);
	case 1:
		return IsInSameRaidWith(p);
	case 2:
		return GetTeam() == p->GetTeam();
	}
}

bool Player::IsInSameGroupWith(Player const* p) const {
	return p == this
			|| (GetGroup() != NULL && GetGroup() == p->GetGroup()
					&& GetGroup()->SameSubGroup(this, p));
}

///- If the player is invited, remove him. If the group if then only 1 person, disband the group.
/// \todo Shouldn't we also check if there is no other invitees before disbanding the group?
void Player::UninviteFromGroup() {
	Group* group = GetGroupInvite();
	if (!group)
		return;

	group->RemoveInvite(this);

	if (group->GetMembersCount() <= 1) // group has just 1 member => disband
			{
		if (group->IsCreated()) {
			group->Disband(true);
			sObjectMgr->RemoveGroup(group);
		} else
			group->RemoveAllInvites();

		delete group;
	}
}

void Player::RemoveFromGroup(Group* group, uint64 guid,
		RemoveMethod method /* = GROUP_REMOVEMETHOD_DEFAULT*/,
		uint64 kicker /* = 0 */, const char* reason /* = NULL */) {
	if (group) {
		if (group->RemoveMember(guid, method, kicker, reason) <= 1) {
			// group->Disband(); already disbanded in RemoveMember
			sObjectMgr->RemoveGroup(group);
			delete group;
			group = NULL;
			// removemember sets the player's group pointer to NULL
		}
	}
}

void Player::SendPlayerMoneyNotify(Player* player, uint32 Money,
		uint32 Modifier) {
	sLog->outDebug(
			LOG_FILTER_NETWORKIO,
			"World: SMSG_LOOT_MONEY_NOTIFY Player %s looted money: %d, Money Modifier: %d , Guild Money: %d",
			player->GetName(), Money, Modifier, Money * Modifier);

	WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, (4 + 4 + 1));
	data << uint32(Money);
	data << uint32(Money * Modifier);
	data << uint8(0);

	player->GetSession()->SendPacket(&data);
}
void Player::SendLogXPGain(uint32 GivenXP, Unit* victim, uint32 BonusXP,
		bool recruitAFriend, float /*group_rate*/) {
	WorldPacket data(SMSG_LOG_XPGAIN, 21); // guess size?
	data << uint64(victim ? victim->GetGUID() : 0); // guid
	data << uint32(GivenXP + BonusXP); // given experience
	data << uint8(victim ? 0 : 1); // 00-kill_xp type, 01-non_kill_xp type

	if (victim) {
		data << uint32(GivenXP); // experience without bonus

		// should use group_rate here but can't figure out how
		data << float(1); // 1 - none 0 - 100% group bonus output
	}

	data << uint8(recruitAFriend ? 1 : 0); // does the GivenXP include a RaF bonus?
	GetSession()->SendPacket(&data);
}

void Player::GiveXP(uint32 xp, Unit *victim, float group_rate) {
	if (xp < 1)
		return;

	if (!isAlive())
		return;

	if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_NO_XP_GAIN))
		return;

	if (victim && victim->GetTypeId() == TYPEID_UNIT
			&& !victim->ToCreature()->hasLootRecipient())
		return;

	uint8 level = getLevel();

	sScriptMgr->OnGivePlayerXP(this, xp, victim);

	// Favored experience increase START
	uint32 zone = GetZoneId();
	float favored_exp_mult = 0;
	if ((HasAura(32096) || HasAura(32098))
			&& (zone == 3483 || zone == 3562 || zone == 3836 || zone == 3713
					|| zone == 3714))
		favored_exp_mult = 0.05f; // Thrallmar's Favor and Honor Hold's Favor
	xp = uint32(xp * (1 + favored_exp_mult));
	// Favored experience increase END

	// XP to money conversion processed in Player::RewardQuest
	if (level >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
		return;

	uint32 bonus_xp = 0;
	bool recruitAFriend = GetsRecruitAFriendBonus(true);

	// RaF does NOT stack with rested experience
	if (recruitAFriend)
		bonus_xp = 2 * xp; // xp + bonus_xp must add up to 3 * xp for RaF; calculation for quests done client-side
	else
		bonus_xp = victim ? GetXPRestBonus(xp) : 0; // XP resting bonus

	SendLogXPGain(xp, victim, bonus_xp, recruitAFriend, group_rate);

	uint32 curXP = GetUInt32Value(PLAYER_XP);
	uint32 nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
	uint32 newXP = curXP + xp + bonus_xp;

	while (newXP >= nextLvlXP
			&& level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) {
		newXP -= nextLvlXP;

		if (level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
			GiveLevel(level + 1);

		level = getLevel();
		nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
	}

	SetUInt32Value(PLAYER_XP, newXP);
}

// Update player to next level
// Current player experience not update (must be update by caller)
void Player::GiveLevel(uint8 level) {
	if (level == getLevel())
		return;

	if (Guild * pGuild = sObjectMgr->GetGuildById(this->GetGuildId()))
		pGuild->UpdateMemberData(this, GUILD_MEMBER_DATA_LEVEL, level);

	sScriptMgr->OnPlayerLevelChanged(this, level);

	PlayerLevelInfo info;
	sObjectMgr->GetPlayerLevelInfo(getRace(), getClass(), level, &info);

	PlayerClassLevelInfo classInfo;
	sObjectMgr->GetPlayerClassLevelInfo(getClass(), level, &classInfo);

	// send levelup info to client
	WorldPacket data(SMSG_LEVELUP_INFO,
			(4 + 4 + MAX_POWERS * 4 + MAX_STATS * 4));
	data << uint32(level);
	data << uint32(int32(classInfo.basehealth) - int32(GetCreateHealth()));
	// for (int i = 0; i < MAX_POWERS; ++i)                  // Powers loop (0-6)
	data << uint32(int32(classInfo.basemana) - int32(GetCreateMana()));
	data << uint32(0);
	data << uint32(0);
	data << uint32(0);
	data << uint32(0);
	data << uint32(0);
	data << uint32(0);
	// end for
	for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i) // Stats loop (0-4)
		data << uint32(int32(info.stats[i]) - GetCreateStat(Stats(i)));

	GetSession()->SendPacket(&data);

	SetUInt32Value(PLAYER_NEXT_LEVEL_XP, sObjectMgr->GetXPForLevel(level));

	//update level, max level of skills
	m_Played_time[PLAYED_TIME_LEVEL] = 0; // Level Played Time reset

	_ApplyAllLevelScaleItemMods(false);

	SetLevel(level);

	UpdateSkillsForLevel();

	// save base values (bonuses already included in stored stats
	for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
		SetCreateStat(Stats(i), info.stats[i]);

	SetCreateHealth(classInfo.basehealth);
	SetCreateMana(classInfo.basemana);

	InitTalentForLevel();
	InitTaxiNodesForLevel();
	InitGlyphsForLevel();

	UpdateAllStats();

	if (sWorld->getBoolConfig(CONFIG_ALWAYS_MAXSKILL)) // Max weapon skill when leveling up
		UpdateSkillsToMaxSkillsForLevel();

	// set current level health and mana/energy to maximum after applying all mods.
	SetFullHealth();
	SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
	SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
	if (GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE))
		SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE));
	SetPower(POWER_FOCUS, 0);
	SetPower(POWER_HAPPINESS, 0);

	_ApplyAllLevelScaleItemMods(true);

	// update level to hunter/summon pet
	if (Pet* pet = GetPet())
		pet->SynchronizeLevelWithOwner();

	if (MailLevelReward const* mailReward = sObjectMgr->GetMailLevelReward(level, getRaceMask())) {
		//- TODO: Poor design of mail system
		SQLTransaction trans = CharacterDatabase.BeginTransaction();
		MailDraft(mailReward->mailTemplateId).SendMailTo(trans, this,
				MailSender(MAIL_CREATURE, mailReward->senderEntry));
		CharacterDatabase.CommitTransaction(trans);
	}

	GetAchievementMgr().UpdateAchievementCriteria(
			ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL);
}

void Player::InitTalentForLevel() {
	uint8 level = getLevel();
	// talents base at level diff (talents = level - 9 but some can be used already)
	if (level < 10) {
		resetTalents(true);
		SetFreeTalentPoints(0);
	} else {
		if (level < sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL)
				|| m_specsCount == 0) {
			m_specsCount = 1;
			m_activeSpec = 0;
		}

		uint32 talentPointsForLevel = CalculateTalentsPoints();

		// if used more that have then reset
		if (m_usedTalentCount > talentPointsForLevel) {
			if (GetSession()->GetSecurity() < SEC_ADMINISTRATOR)
				resetTalents(true);
			else
				SetFreeTalentPoints(0);
		}
		// else update amount of free points
		else
			SetFreeTalentPoints(talentPointsForLevel - m_usedTalentCount);
	}

	if (!GetSession()->PlayerLoading())
		SendTalentsInfoData(false); // update at client
}

void Player::InitStatsForLevel(bool reapplyMods) {
	if (reapplyMods) //reapply stats values only on .reset stats (level) command
		_RemoveAllStatBonuses();

	PlayerClassLevelInfo classInfo;
	sObjectMgr->GetPlayerClassLevelInfo(getClass(), getLevel(), &classInfo);

	PlayerLevelInfo info;
	sObjectMgr->GetPlayerLevelInfo(getRace(), getClass(), getLevel(), &info);

	SetUInt32Value(PLAYER_FIELD_MAX_LEVEL,
			sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
	SetUInt32Value(PLAYER_NEXT_LEVEL_XP, sObjectMgr->GetXPForLevel(getLevel()));

	// reset before any aura state sources (health set/aura apply)
	SetUInt32Value(UNIT_FIELD_AURASTATE, 0);

	UpdateSkillsForLevel();

	// set default cast time multiplier
	SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);

	// reset size before reapply auras
	SetFloatValue(OBJECT_FIELD_SCALE_X, 1.0f);

	// save base values (bonuses already included in stored stats
	for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
		SetCreateStat(Stats(i), info.stats[i]);

	for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
		SetStat(Stats(i), info.stats[i]);

	SetCreateHealth(classInfo.basehealth);

	//set create powers
	SetCreateMana(classInfo.basemana);

	SetArmor(0);

	InitStatBuffMods();

	//reset rating fields values
	for (uint16 index = PLAYER_FIELD_COMBAT_RATING_1;
			index < PLAYER_FIELD_COMBAT_RATING_1 + MAX_COMBAT_RATING; ++index)
		SetUInt32Value(index, 0);

	SetUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, 0);
	SetFloatValue(PLAYER_FIELD_MOD_HEALING_PCT, 1.0f);
	SetFloatValue(PLAYER_FIELD_MOD_HEALING_DONE_PCT, 1.0f);
	for (uint8 i = 0; i < 7; ++i) {
		SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + i, 0);
		SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + i, 0);
		SetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT + i, 1.00f);
	}
	SetFloatValue(PLAYER_FIELD_MOD_SPELL_POWER_PCT, 1.0f);

	//reset attack power, damage and attack speed fields
	SetFloatValue(UNIT_FIELD_BASEATTACKTIME, 2000.0f);
	SetFloatValue(UNIT_FIELD_BASEATTACKTIME + 1, 2000.0f); // offhand attack time
	SetFloatValue(UNIT_FIELD_RANGEDATTACKTIME, 2000.0f);

	SetFloatValue(UNIT_FIELD_MINDAMAGE, 0.0f);
	SetFloatValue(UNIT_FIELD_MAXDAMAGE, 0.0f);
	SetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE, 0.0f);
	SetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE, 0.0f);
	SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE, 0.0f);
	SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE, 0.0f);

	SetInt32Value(UNIT_FIELD_ATTACK_POWER, 0);
	SetInt32Value(UNIT_FIELD_ATTACK_POWER_MOD_POS, 0);
	SetInt32Value(UNIT_FIELD_ATTACK_POWER_MOD_NEG, 0);
	SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, 0.0f);
	SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER, 0);
	SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MOD_POS, 0);
	SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MOD_NEG, 0);
	SetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER, 0.0f);

	// Base crit values (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
	SetFloatValue(PLAYER_CRIT_PERCENTAGE, 0.0f);
	SetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE, 0.0f);
	SetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE, 0.0f);

	// Init spell schools (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
	for (uint8 i = 0; i < 7; ++i)
		SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1 + i, 0.0f);

	SetFloatValue(PLAYER_PARRY_PERCENTAGE, 0.0f);
	SetFloatValue(PLAYER_BLOCK_PERCENTAGE, 0.0f);
	SetUInt32Value(PLAYER_SHIELD_BLOCK, 0);

	// Dodge percentage
	SetFloatValue(PLAYER_DODGE_PERCENTAGE, 0.0f);

	// set armor (resistance 0) to original value (0)
	SetArmor(0);
	SetResistanceBuffMods(SpellSchools(0), true, 0.0f);
	SetResistanceBuffMods(SpellSchools(0), false, 0.0f);
	// set other resistance to original value (0)
	for (uint8 i = 1; i < MAX_SPELL_SCHOOL; ++i) {
		SetResistance(SpellSchools(i), 0);
		SetResistanceBuffMods(SpellSchools(i), true, 0.0f);
		SetResistanceBuffMods(SpellSchools(i), false, 0.0f);
	}

	SetUInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE, 0);
	SetUInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE, 0);
	for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i) {
		SetUInt32Value(UNIT_FIELD_POWER_COST_MODIFIER + i, 0);
		SetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER + i, 0.0f);
	}
	// Reset no reagent cost field
	for (uint8 i = 0; i < 3; ++i)
		SetUInt32Value(PLAYER_NO_REAGENT_COST_1 + i, 0);
	// Init data for form but skip reapply item mods for form
	InitDataForForm(reapplyMods);

	// save new stats
	for (uint8 i = POWER_MANA; i < MAX_POWERS; ++i)
		SetMaxPower(Powers(i), uint32(GetCreatePowers(Powers(i))));

	SetMaxHealth(classInfo.basehealth); // stamina bonus will applied later

	// cleanup mounted state (it will set correctly at aura loading if player saved at mount.
	SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0);

	// cleanup unit flags (will be re-applied if need at aura load).
	RemoveFlag(
			UNIT_FIELD_FLAGS,
			UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE
					| UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_OOC_NOT_ATTACKABLE
					| UNIT_FLAG_PASSIVE | UNIT_FLAG_LOOTING
					| UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_SILENCED
					| UNIT_FLAG_PACIFIED | UNIT_FLAG_STUNNED
					| UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED
					| UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING
					| UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_SKINNABLE
					| UNIT_FLAG_MOUNT | UNIT_FLAG_TAXI_FLIGHT);
	SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); // must be set

	SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER); // must be set

	// cleanup player flags (will be re-applied if need at aura load), to avoid have ghost flag without ghost aura, for example.
	RemoveFlag(
			PLAYER_FLAGS,
			PLAYER_FLAGS_AFK | PLAYER_FLAGS_DND | PLAYER_FLAGS_DEVELOPER
					| PLAYER_FLAGS_GM | PLAYER_FLAGS_GHOST
					| PLAYER_ALLOW_ONLY_ABILITY);

	RemoveStandFlags(UNIT_STAND_FLAGS_ALL); // one form stealth modified bytes
	RemoveByteFlag(UNIT_FIELD_BYTES_2, 1,
			UNIT_BYTE2_FLAG_FFA_PVP | UNIT_BYTE2_FLAG_SANCTUARY);

	// restore if need some important flags
	SetUInt32Value(PLAYER_FIELD_BYTES2, 0); // flags empty by default

	if (reapplyMods) // reapply stats values only on .reset stats (level) command
		_ApplyAllStatBonuses();

	// set current level health and mana/energy to maximum after applying all mods.
	SetFullHealth();
	SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
	SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
	if (GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE))
		SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE));
	SetPower(POWER_FOCUS, 0);
	SetPower(POWER_HAPPINESS, 0);
	SetPower(POWER_RUNIC_POWER, 0);

	// update level to hunter/summon pet
	if (Pet* pet = GetPet())
		pet->SynchronizeLevelWithOwner();
}

void Player::SendInitialSpells() {
	time_t curTime = time(NULL);
	time_t infTime = curTime + infinityCooldownDelayCheck;

	uint16 spellCount = 0;

	WorldPacket data(
			SMSG_INITIAL_SPELLS,
			(1 + 2 + 4 * m_spells.size() + 2
					+ m_spellCooldowns.size() * (2 + 2 + 2 + 4 + 4)));
	data << uint8(0);

	size_t countPos = data.wpos();
	data << uint16(spellCount); // spell count placeholder

	for (PlayerSpellMap::const_iterator itr = m_spells.begin();
			itr != m_spells.end(); ++itr) {
		if (itr->second->state == PLAYERSPELL_REMOVED)
			continue;

		if (!itr->second->active || itr->second->disabled)
			continue;

		data << uint32(itr->first);
		data << uint16(0); // it's not slot id

		spellCount += 1;
	}

	data.put<uint16>(countPos, spellCount); // write real count value

	uint16 spellCooldowns = m_spellCooldowns.size();
	data << uint16(spellCooldowns);
	for (SpellCooldowns::const_iterator itr = m_spellCooldowns.begin();
			itr != m_spellCooldowns.end(); ++itr) {
		SpellEntry const *sEntry = sSpellStore.LookupEntry(itr->first);
		if (!sEntry)
			continue;

		data << uint32(itr->first);

		data << uint16(itr->second.itemid); // cast item id
		data << uint16(sEntry->Category); // spell category

		// send infinity cooldown in special format
		if (itr->second.end >= infTime) {
			data << uint32(1); // cooldown
			data << uint32(0x80000000); // category cooldown
			continue;
		}

		time_t cooldown =
				itr->second.end > curTime ?
						(itr->second.end - curTime) * IN_MILLISECONDS : 0;

		if (sEntry->Category) // may be wrong, but anyway better than nothing...
		{
			data << uint32(0); // cooldown
			data << uint32(cooldown); // category cooldown
		} else {
			data << uint32(cooldown); // cooldown
			data << uint32(0); // category cooldown
		}
	}

	GetSession()->SendPacket(&data);

	sLog->outDetail("CHARACTER: Sent Initial Spells");
}

void Player::RemoveMail(uint32 id) {
	for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();
			++itr) {
		if ((*itr)->messageID == id) {
			//do not delete item, because Player::removeMail() is called when returning mail to sender.
			m_mail.erase(itr);
			return;
		}
	}
}

void Player::SendMailResult(uint32 mailId, MailResponseType mailAction,
		MailResponseResult mailError, uint32 equipError, uint32 item_guid,
		uint32 item_count) {
	WorldPacket data(
			SMSG_SEND_MAIL_RESULT,
			(4 + 4 + 4
					+ (mailError == MAIL_ERR_EQUIP_ERROR ?
							4 : (mailAction == MAIL_ITEM_TAKEN ? 4 + 4 : 0))));
	data << (uint32) mailId;
	data << (uint32) mailAction;
	data << (uint32) mailError;
	if (mailError == MAIL_ERR_EQUIP_ERROR)
		data << (uint32) equipError;
	else if (mailAction == MAIL_ITEM_TAKEN) {
		data << (uint32) item_guid; // item guid low?
		data << (uint32) item_count; // item count?
	}
	GetSession()->SendPacket(&data);
}

void Player::SendNewMail() {
	// deliver undelivered mail
	WorldPacket data(SMSG_RECEIVED_MAIL, 4);
	data << (uint32) 0;
	GetSession()->SendPacket(&data);
}

void Player::UpdateNextMailTimeAndUnreads() {
	// calculate next delivery time (min. from non-delivered mails
	// and recalculate unReadMail
	time_t cTime = time(NULL);
	m_nextMailDelivereTime = 0;
	unReadMails = 0;
	for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();
			++itr) {
		if ((*itr)->deliver_time > cTime) {
			if (!m_nextMailDelivereTime
					|| m_nextMailDelivereTime > (*itr)->deliver_time)
				m_nextMailDelivereTime = (*itr)->deliver_time;
		} else if (((*itr)->checked & MAIL_CHECK_MASK_READ) == 0)
			++unReadMails;
	}
}

void Player::AddNewMailDeliverTime(time_t deliver_time) {
	if (deliver_time <= time(NULL)) // ready now
			{
		++unReadMails;
		SendNewMail();
	} else // not ready and no have ready mails
	{
		if (!m_nextMailDelivereTime || m_nextMailDelivereTime > deliver_time)
			m_nextMailDelivereTime = deliver_time;
	}
}

bool Player::AddTalent(uint32 spell_id, uint8 spec, bool learning) {
	SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
	if (!spellInfo) {
		// do character spell book cleanup (all characters)
		if (!IsInWorld() && !learning) // spell load case
				{
			sLog->outError(
					"Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.",
					spell_id);
			CharacterDatabase.PExecute(
					"DELETE FROM character_talent WHERE spell = '%u'",
					spell_id);
		} else
			sLog->outError(
					"Player::addSpell: Non-existed in SpellStore spell #%u request.",
					spell_id);

		return false;
	}

	if (!SpellMgr::IsSpellValid(spellInfo, this, false)) {
		// do character spell book cleanup (all characters)
		if (!IsInWorld() && !learning) // spell load case
				{
			sLog->outError(
					"Player::addTalent: Broken spell #%u learning not allowed, deleting for all characters in `character_talent`.",
					spell_id);
			CharacterDatabase.PExecute(
					"DELETE FROM character_talent WHERE spell = '%u'",
					spell_id);
		} else
			sLog->outError(
					"Player::addTalent: Broken spell #%u learning not allowed.",
					spell_id);

		return false;
	}

	PlayerTalentMap::iterator itr = m_talents[spec]->find(spell_id);
	if (itr != m_talents[spec]->end())
		itr->second->state = PLAYERSPELL_UNCHANGED;
	else if (TalentSpellPos const *talentPos = GetTalentSpellPos(spell_id)) {
		if (TalentEntry const *talentInfo = sTalentStore.LookupEntry(talentPos->talent_id)) {
			for (uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank) {
				// skip learning spell and no rank spell case
				uint32 rankSpellId = talentInfo->RankID[rank];
				if (!rankSpellId || rankSpellId == spell_id)
					continue;

				PlayerTalentMap::iterator itr = m_talents[spec]->find(
						rankSpellId);
				if (itr != m_talents[spec]->end())
					itr->second->state = PLAYERSPELL_REMOVED;
			}
		}

		PlayerSpellState state =
				learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
		PlayerTalent *newtalent = new PlayerTalent();

		newtalent->state = state;
		newtalent->spec = spec;

		(*m_talents[spec])[spell_id] = newtalent;
		return true;
	}
	return false;
}

bool Player::addSpell(uint32 spell_id, bool active, bool learning,
		bool dependent, bool disabled) {
	SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
	if (!spellInfo) {
		// do character spell book cleanup (all characters)
		if (!IsInWorld() && !learning) // spell load case
				{
			sLog->outError(
					"Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.",
					spell_id);
			CharacterDatabase.PExecute(
					"DELETE FROM character_spell WHERE spell = '%u'", spell_id);
		} else
			sLog->outError(
					"Player::addSpell: Non-existed in SpellStore spell #%u request.",
					spell_id);

		return false;
	}

	if (!SpellMgr::IsSpellValid(spellInfo, this, false)) {
		// do character spell book cleanup (all characters)
		if (!IsInWorld() && !learning) // spell load case
				{
			sLog->outError(
					"Player::addSpell: Broken spell #%u learning not allowed, deleting for all characters in `character_spell`.",
					spell_id);
			CharacterDatabase.PExecute(
					"DELETE FROM character_spell WHERE spell = '%u'", spell_id);
		} else
			sLog->outError(
					"Player::addSpell: Broken spell #%u learning not allowed.",
					spell_id);

		return false;
	}

	PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;

	bool dependent_set = false;
	bool disabled_case = false;
	bool superceded_old = false;

	PlayerSpellMap::iterator itr = m_spells.find(spell_id);

	// Remove temporary spell if found to prevent conflicts
	if (itr != m_spells.end() && itr->second->state == PLAYERSPELL_TEMPORARY)
		RemoveTemporarySpell(spell_id);
	else if (itr != m_spells.end()) {
		uint32 next_active_spell_id = 0;
		// fix activate state for non-stackable low rank (and find next spell for !active case)
		if (!SpellMgr::canStackSpellRanks(spellInfo)
				&& sSpellMgr->GetSpellRank(spellInfo->Id) != 0) {
			if (uint32 next = sSpellMgr->GetNextSpellInChain(spell_id)) {
				if (HasSpell(next)) {
					// high rank already known so this must !active
					active = false;
					next_active_spell_id = next;
				}
			}
		}

		// not do anything if already known in expected state
		if (itr->second->state != PLAYERSPELL_REMOVED
				&& itr->second->active == active
				&& itr->second->dependent == dependent
				&& itr->second->disabled == disabled) {
			if (!IsInWorld() && !learning) // explicitly load from DB and then exist in it already and set correctly
				itr->second->state = PLAYERSPELL_UNCHANGED;

			return false;
		}

		// dependent spell known as not dependent, overwrite state
		if (itr->second->state != PLAYERSPELL_REMOVED && !itr->second->dependent
				&& dependent) {
			itr->second->dependent = dependent;
			if (itr->second->state != PLAYERSPELL_NEW)
				itr->second->state = PLAYERSPELL_CHANGED;
			dependent_set = true;
		}

		// update active state for known spell
		if (itr->second->active != active
				&& itr->second->state != PLAYERSPELL_REMOVED
				&& !itr->second->disabled) {
			itr->second->active = active;

			if (!IsInWorld() && !learning && !dependent_set) // explicitly load from DB and then exist in it already and set correctly
				itr->second->state = PLAYERSPELL_UNCHANGED;
			else if (itr->second->state != PLAYERSPELL_NEW)
				itr->second->state = PLAYERSPELL_CHANGED;

			if (active) {
				if (IsPassiveSpell(spell_id)
						&& IsNeedCastPassiveSpellAtLearn(spellInfo))
					CastSpell(this, spell_id, true);
			} else if (IsInWorld()) {
				if (next_active_spell_id) {
					// update spell ranks in spellbook and action bar
					WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4);
					data << uint32(spell_id);
					data << uint32(next_active_spell_id);
					GetSession()->SendPacket(&data);
				} else {
					WorldPacket data(SMSG_REMOVED_SPELL, 4);
					data << uint32(spell_id);
					GetSession()->SendPacket(&data);
				}
			}

			return active; // learn (show in spell book if active now)
		}

		if (itr->second->disabled != disabled
				&& itr->second->state != PLAYERSPELL_REMOVED) {
			if (itr->second->state != PLAYERSPELL_NEW)
				itr->second->state = PLAYERSPELL_CHANGED;
			itr->second->disabled = disabled;

			if (disabled)
				return false;

			disabled_case = true;
		} else
			switch (itr->second->state) {
			case PLAYERSPELL_UNCHANGED: // known saved spell
				return false;
			case PLAYERSPELL_REMOVED: // re-learning removed not saved spell
			{
				delete itr->second;
				m_spells.erase(itr);
				state = PLAYERSPELL_CHANGED;
				break; // need re-add
			}
			default: // known not saved yet spell (new or modified)
			{
				// can be in case spell loading but learned at some previous spell loading
				if (!IsInWorld() && !learning && !dependent_set)
					itr->second->state = PLAYERSPELL_UNCHANGED;

				return false;
			}
			}
	}

	if (!disabled_case) // skip new spell adding if spell already known (disabled spells case)
	{
		// talent: unlearn all other talent ranks (high and low)
		if (TalentSpellPos const *talentPos = GetTalentSpellPos(spell_id)) {
			if (TalentEntry const *talentInfo = sTalentStore.LookupEntry(talentPos->talent_id)) {
				for (uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank) {
					// skip learning spell and no rank spell case
					uint32 rankSpellId = talentInfo->RankID[rank];
					if (!rankSpellId || rankSpellId == spell_id)
						continue;

					removeSpell(rankSpellId, false, false);
				}
			}
		}
		// non talent spell: learn low ranks (recursive call)
		else if (uint32 prev_spell = sSpellMgr->GetPrevSpellInChain(spell_id)) {
			if (!IsInWorld() || disabled) // at spells loading, no output, but allow save
				addSpell(prev_spell, active, true, true, disabled);
			else
				// at normal learning
				learnSpell(prev_spell, true);
		}

		PlayerSpell *newspell = new PlayerSpell;
		newspell->state = state;
		newspell->active = active;
		newspell->dependent = dependent;
		newspell->disabled = disabled;

		// replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible
		if (newspell->active && !newspell->disabled
				&& !SpellMgr::canStackSpellRanks(spellInfo)
				&& sSpellMgr->GetSpellRank(spellInfo->Id) != 0) {
			for (PlayerSpellMap::iterator itr2 = m_spells.begin();
					itr2 != m_spells.end(); ++itr2) {
				if (itr2->second->state == PLAYERSPELL_REMOVED)
					continue;
				SpellEntry const *i_spellInfo = sSpellStore.LookupEntry(
						itr2->first);
				if (!i_spellInfo)
					continue;

				if (sSpellMgr->IsRankSpellDueToSpell(spellInfo, itr2->first)) {
					if (itr2->second->active) {
						if (sSpellMgr->IsHighRankOfSpell(spell_id,
								itr2->first)) {
							if (IsInWorld()) // not send spell (re-/over-)learn packets at loading
							{
								WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4);
								data << uint32(itr2->first);
								data << uint32(spell_id);
								GetSession()->SendPacket(&data);
							}

							// mark old spell as disable (SMSG_SUPERCEDED_SPELL replace it in client by new)
							itr2->second->active = false;
							if (itr2->second->state != PLAYERSPELL_NEW)
								itr2->second->state = PLAYERSPELL_CHANGED;
							superceded_old = true; // new spell replace old in action bars and spell book.
						} else if (sSpellMgr->IsHighRankOfSpell(itr2->first,
								spell_id)) {
							if (IsInWorld()) // not send spell (re-/over-)learn packets at loading
							{
								WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4);
								data << uint32(spell_id);
								data << uint32(itr2->first);
								GetSession()->SendPacket(&data);
							}

							// mark new spell as disable (not learned yet for client and will not learned)
							newspell->active = false;
							if (newspell->state != PLAYERSPELL_NEW)
								newspell->state = PLAYERSPELL_CHANGED;
						}
					}
				}
			}
		}

		m_spells[spell_id] = newspell;

		// return false if spell disabled
		if (newspell->disabled)
			return false;
	}

	uint32 talentCost = GetTalentSpellCost(spell_id);

	// cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
	// note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
	if (talentCost > 0
			&& IsSpellHaveEffect(spellInfo, SPELL_EFFECT_LEARN_SPELL)) {
		// ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
		CastSpell(this, spell_id, true);
	}
	// also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks
	else if (IsPassiveSpell(spell_id)) {
		if (IsNeedCastPassiveSpellAtLearn(spellInfo))
			CastSpell(this, spell_id, true);
	} else if (IsSpellHaveEffect(spellInfo, SPELL_EFFECT_SKILL_STEP)) {
		CastSpell(this, spell_id, true);
		return false;
	}

	// update used talent points count
	m_usedTalentCount += talentCost;

	// update free primary prof.points (if any, can be none in case GM .learn prof. learning)
	if (uint32 freeProfs = GetFreePrimaryProfessionPoints()) {
		if (sSpellMgr->IsPrimaryProfessionFirstRankSpell(spell_id))
			SetFreePrimaryProfessions(freeProfs - 1);
	}

	// add dependent skills
	uint16 maxskill = GetMaxSkillValueForLevel();

	SpellLearnSkillNode const* spellLearnSkill = sSpellMgr->GetSpellLearnSkill(
			spell_id);

	SkillLineAbilityMapBounds skill_bounds =
			sSpellMgr->GetSkillLineAbilityMapBounds(spell_id);

	if (spellLearnSkill) {
		uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill);
		uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill);

		if (skill_value < spellLearnSkill->value)
			skill_value = spellLearnSkill->value;

		uint32 new_skill_max_value =
				spellLearnSkill->maxvalue == 0 ?
						maxskill : spellLearnSkill->maxvalue;

		if (skill_max_value < new_skill_max_value)
			skill_max_value = new_skill_max_value;

		SetSkill(spellLearnSkill->skill, spellLearnSkill->step, skill_value,
				skill_max_value);
	} else {
		// not ranked skills
		for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first;
				_spell_idx != skill_bounds.second; ++_spell_idx) {
			SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(
					_spell_idx->second->skillId);
			if (!pSkill)
				continue;

			if (!Has310Flyer(false) && pSkill->id == SKILL_MOUNTS)
				for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
					if (spellInfo->EffectApplyAuraName[i]
							== SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED
							&& SpellMgr::CalculateSpellEffectAmount(spellInfo,
									i) == 310)
						SetHas310Flyer(true);

			if (HasSkill(pSkill->id))
				continue;

			if (_spell_idx->second->learnOnGetSkill
					== ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL ||
			// lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
					((pSkill->id == SKILL_LOCKPICKING
							|| pSkill->id == SKILL_RUNEFORGING)
							&& _spell_idx->second->max_value == 0)) {
				switch (GetSkillRangeType(pSkill,
						_spell_idx->second->racemask != 0)) {
				case SKILL_RANGE_LANGUAGE:
					SetSkill(pSkill->id, GetSkillStep(pSkill->id), 300, 300);
					break;
				case SKILL_RANGE_LEVEL:
					SetSkill(pSkill->id, GetSkillStep(pSkill->id), 1,
							GetMaxSkillValueForLevel());
					break;
				case SKILL_RANGE_MONO:
					SetSkill(pSkill->id, GetSkillStep(pSkill->id), 1, 1);
					break;
				default:
					break;
				}
			}
		}
	}

	// learn dependent spells
	SpellLearnSpellMapBounds spell_bounds =
			sSpellMgr->GetSpellLearnSpellMapBounds(spell_id);

	for (SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first;
			itr2 != spell_bounds.second; ++itr2) {
		if (!itr2->second.autoLearned) {
			if (!IsInWorld() || !itr2->second.active) // at spells loading, no output, but allow save
				addSpell(itr2->second.spell, itr2->second.active, true, true,
						false);
			else
				// at normal learning
				learnSpell(itr2->second.spell, true);
		}
	}

	if (!GetSession()->PlayerLoading()) {
		// not ranked skills
		for (SkillLineAbilityMap::const_iterator _spell_idx = skill_bounds.first;
				_spell_idx != skill_bounds.second; ++_spell_idx) {
			GetAchievementMgr().UpdateAchievementCriteria(
					ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE,
					_spell_idx->second->skillId);
			GetAchievementMgr().UpdateAchievementCriteria(
					ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS,
					_spell_idx->second->skillId);
		}

		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL, spell_id);
	}

	// return true (for send learn packet) only if spell active (in case ranked spells) and not replace old spell
	return active && !disabled && !superceded_old;
}

void Player::AddTemporarySpell(uint32 spellId) {
	PlayerSpellMap::iterator itr = m_spells.find(spellId);
	// spell already added - do not do anything
	if (itr != m_spells.end())
		return;
	PlayerSpell *newspell = new PlayerSpell;
	newspell->state = PLAYERSPELL_TEMPORARY;
	newspell->active = true;
	newspell->dependent = false;
	newspell->disabled = false;
	m_spells[spellId] = newspell;
}

void Player::RemoveTemporarySpell(uint32 spellId) {
	PlayerSpellMap::iterator itr = m_spells.find(spellId);
	// spell already not in list - do not do anything
	if (itr == m_spells.end())
		return;
	// spell has other state than temporary - do not change it
	if (itr->second->state != PLAYERSPELL_TEMPORARY)
		return;
	delete itr->second;
	m_spells.erase(itr);
}

bool Player::IsNeedCastPassiveSpellAtLearn(SpellEntry const* spellInfo) const {
	// note: form passives activated with shapeshift spells be implemented by HandleShapeshiftBoosts instead of spell_learn_spell
	// talent dependent passives activated at form apply have proper stance data
	ShapeshiftForm form = GetShapeshiftForm();
	bool need_cast = (!spellInfo->Stances
			|| (form && (spellInfo->Stances & (1 << (form - 1))))
			|| (!form
					&& (spellInfo->AttributesEx2
							& SPELL_ATTR2_NOT_NEED_SHAPESHIFT)));

	//Check CasterAuraStates
	return need_cast
			&& (!spellInfo->CasterAuraState
					|| HasAuraState(AuraState(spellInfo->CasterAuraState)));
}

void Player::learnSpell(uint32 spell_id, bool dependent) {
	PlayerSpellMap::iterator itr = m_spells.find(spell_id);

	bool disabled = (itr != m_spells.end()) ? itr->second->disabled : false;
	bool active = disabled ? itr->second->active : true;

	bool learning = addSpell(spell_id, active, true, dependent, false);

	// prevent duplicated entires in spell book, also not send if not in world (loading)
	if (learning && IsInWorld()) {
		WorldPacket data(SMSG_LEARNED_SPELL, 6);
		data << uint32(spell_id);
		data << uint16(0);
		GetSession()->SendPacket(&data);
	}

	// learn all disabled higher ranks and required spells (recursive)
	if (disabled) {
		SpellChainNode const* node = sSpellMgr->GetSpellChainNode(spell_id);
		if (node) {
			PlayerSpellMap::iterator iter = m_spells.find(node->next);
			if (iter != m_spells.end() && iter->second->disabled)
				learnSpell(node->next, false);
		}

		SpellsRequiringSpellMapBounds spellsRequiringSpell =
				sSpellMgr->GetSpellsRequiringSpellBounds(spell_id);
		for (SpellsRequiringSpellMap::const_iterator itr2 =
				spellsRequiringSpell.first; itr2 != spellsRequiringSpell.second;
				++itr2) {
			PlayerSpellMap::iterator iter2 = m_spells.find(itr2->second);
			if (iter2 != m_spells.end() && iter2->second->disabled)
				learnSpell(itr2->second, false);
		}
	}
}

void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank) {
	PlayerSpellMap::iterator itr = m_spells.find(spell_id);
	if (itr == m_spells.end())
		return;

	if (itr->second->state == PLAYERSPELL_REMOVED
			|| (disabled && itr->second->disabled)
			|| itr->second->state == PLAYERSPELL_TEMPORARY)
		return;

	// unlearn non talent higher ranks (recursive)
	if (SpellChainNode const* node = sSpellMgr->GetSpellChainNode(spell_id)) {
		if (HasSpell(node->next) && !GetTalentSpellPos(node->next))
			removeSpell(node->next, disabled, false);
	}
	//unlearn spells dependent from recently removed spells
	SpellsRequiringSpellMapBounds spellsRequiringSpell =
			sSpellMgr->GetSpellsRequiringSpellBounds(spell_id);
	for (SpellsRequiringSpellMap::const_iterator itr2 =
			spellsRequiringSpell.first; itr2 != spellsRequiringSpell.second;
			++itr2)
		removeSpell(itr2->second, disabled);

	// re-search, it can be corrupted in prev loop
	itr = m_spells.find(spell_id);
	if (itr == m_spells.end())
		return; // already unleared

	bool giveTalentPoints = disabled || !itr->second->disabled;

	bool cur_active = itr->second->active;
	bool cur_dependent = itr->second->dependent;

	if (disabled) {
		itr->second->disabled = disabled;
		if (itr->second->state != PLAYERSPELL_NEW)
			itr->second->state = PLAYERSPELL_CHANGED;
	} else {
		if (itr->second->state == PLAYERSPELL_NEW) {
			delete itr->second;
			m_spells.erase(itr);
		} else
			itr->second->state = PLAYERSPELL_REMOVED;
	}

	RemoveAurasDueToSpell(spell_id);

	// remove pet auras
	for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
		if (PetAura const *petSpell = sSpellMgr->GetPetAura(spell_id, i))
			RemovePetAura(petSpell);

	// free talent points
	uint32 talentCosts = GetTalentSpellCost(spell_id);
	if (talentCosts > 0 && giveTalentPoints) {
		if (talentCosts < m_usedTalentCount)
			m_usedTalentCount -= talentCosts;
		else
			m_usedTalentCount = 0;
	}

	// update free primary prof.points (if not overflow setting, can be in case GM use before .learn prof. learning)
	if (sSpellMgr->IsPrimaryProfessionFirstRankSpell(spell_id)) {
		uint32 freeProfs = GetFreePrimaryProfessionPoints() + 1;
		if (freeProfs <= sWorld->getIntConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL))
			SetFreePrimaryProfessions(freeProfs);
	}

	// remove dependent skill
	SpellLearnSkillNode const *spellLearnSkill = sSpellMgr->GetSpellLearnSkill(
			spell_id);
	if (spellLearnSkill) {
		uint32 prev_spell = sSpellMgr->GetPrevSpellInChain(spell_id);
		if (!prev_spell) // first rank, remove skill
			SetSkill(spellLearnSkill->skill, 0, 0, 0);
		else {
			// search prev. skill setting by spell ranks chain
			SpellLearnSkillNode const *prevSkill =
					sSpellMgr->GetSpellLearnSkill(prev_spell);
			while (!prevSkill && prev_spell) {
				prev_spell = sSpellMgr->GetPrevSpellInChain(prev_spell);
				prevSkill = sSpellMgr->GetSpellLearnSkill(
						sSpellMgr->GetFirstSpellInChain(prev_spell));
			}

			if (!prevSkill) // not found prev skill setting, remove skill
				SetSkill(spellLearnSkill->skill, 0, 0, 0);
			else // set to prev. skill setting values
			{
				uint32 skill_value = GetPureSkillValue(prevSkill->skill);
				uint32 skill_max_value = GetPureMaxSkillValue(prevSkill->skill);

				if (skill_value > prevSkill->value)
					skill_value = prevSkill->value;

				uint32 new_skill_max_value =
						prevSkill->maxvalue == 0 ?
								GetMaxSkillValueForLevel() :
								prevSkill->maxvalue;

				if (skill_max_value > new_skill_max_value)
					skill_max_value = new_skill_max_value;

				SetSkill(prevSkill->skill, prevSkill->step, skill_value,
						skill_max_value);
			}
		}
	} else {
		// not ranked skills
		SkillLineAbilityMapBounds bounds =
				sSpellMgr->GetSkillLineAbilityMapBounds(spell_id);

		for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first;
				_spell_idx != bounds.second; ++_spell_idx) {
			SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(
					_spell_idx->second->skillId);
			if (!pSkill)
				continue;

			if ((_spell_idx->second->learnOnGetSkill
					== ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
					&& pSkill->categoryId != SKILL_CATEGORY_CLASS) || // not unlearn class skills (spellbook/talent pages)
					// lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
					((pSkill->id == SKILL_LOCKPICKING
							|| pSkill->id == SKILL_RUNEFORGING)
							&& _spell_idx->second->max_value == 0)) {
				// not reset skills for professions and racial abilities
				if ((pSkill->categoryId == SKILL_CATEGORY_SECONDARY
						|| pSkill->categoryId == SKILL_CATEGORY_PROFESSION)
						&& (IsProfessionSkill(pSkill->id)
								|| _spell_idx->second->racemask != 0))
					continue;

				SetSkill(pSkill->id, GetSkillStep(pSkill->id), 0, 0);
			}

			// most likely will never be used, haven't heard of cases where players unlearn a mount
			if (Has310Flyer(false)
					&& _spell_idx->second->skillId == SKILL_MOUNTS) {
				SpellEntry const *pSpellInfo = sSpellStore.LookupEntry(
						spell_id);
				for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
					if (pSpellInfo->EffectApplyAuraName[i]
							== SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED
							&& SpellMgr::CalculateSpellEffectAmount(pSpellInfo,
									i) == 310)
						Has310Flyer(true, spell_id); // with true as first argument its also used to set/remove the flag
			}
		}
	}

	// remove dependent spells
	SpellLearnSpellMapBounds spell_bounds =
			sSpellMgr->GetSpellLearnSpellMapBounds(spell_id);

	for (SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first;
			itr2 != spell_bounds.second; ++itr2)
		removeSpell(itr2->second.spell, disabled);

	// activate lesser rank in spellbook/action bar, and cast it if need
	bool prev_activate = false;

	if (uint32 prev_id = sSpellMgr->GetPrevSpellInChain (spell_id)) {
		SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);

		// if talent then lesser rank also talent and need learn
		if (talentCosts) {
			// I cannot see why mangos has these lines.
			//if (learn_low_rank)
			//    learnSpell(prev_id, false);
		}
		// if ranked non-stackable spell: need activate lesser rank and update dendence state
		else if (cur_active && !SpellMgr::canStackSpellRanks(spellInfo)
				&& sSpellMgr->GetSpellRank(spellInfo->Id) != 0) {
			// need manually update dependence state (learn spell ignore like attempts)
			PlayerSpellMap::iterator prev_itr = m_spells.find(prev_id);
			if (prev_itr != m_spells.end()) {
				if (prev_itr->second->dependent != cur_dependent) {
					prev_itr->second->dependent = cur_dependent;
					if (prev_itr->second->state != PLAYERSPELL_NEW)
						prev_itr->second->state = PLAYERSPELL_CHANGED;
				}

				// now re-learn if need re-activate
				if (cur_active && !prev_itr->second->active && learn_low_rank) {
					if (addSpell(prev_id, true, false,
							prev_itr->second->dependent,
							prev_itr->second->disabled)) {
						// downgrade spell ranks in spellbook and action bar
						WorldPacket data(SMSG_SUPERCEDED_SPELL, 4 + 4);
						data << uint32(spell_id);
						data << uint32(prev_id);
						GetSession()->SendPacket(&data);
						prev_activate = true;
					}
				}
			}
		}
	}

	if (spell_id == 46917 && m_canTitanGrip)
		SetCanTitanGrip(false);
	if (spell_id == 674 && m_canDualWield)
		SetCanDualWield(false);

	if (sWorld->getBoolConfig(CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN))
		AutoUnequipOffhandIfNeed();

	// remove from spell book if not replaced by lesser rank
	if (!prev_activate) {
		WorldPacket data(SMSG_REMOVED_SPELL, 4);
		data << uint32(spell_id);
		GetSession()->SendPacket(&data);
	}
}

bool Player::Has310Flyer(bool checkAllSpells, uint32 excludeSpellId) {
	if (!checkAllSpells)
		return m_ExtraFlags & PLAYER_EXTRA_HAS_310_FLYER;
	else {
		SetHas310Flyer(false);
		SpellEntry const *pSpellInfo;
		for (PlayerSpellMap::iterator itr = m_spells.begin();
				itr != m_spells.end(); ++itr) {
			if (itr->first == excludeSpellId)
				continue;

			SkillLineAbilityMapBounds bounds =
					sSpellMgr->GetSkillLineAbilityMapBounds(itr->first);
			for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first;
					_spell_idx != bounds.second; ++_spell_idx) {
				if (_spell_idx->second->skillId != SKILL_MOUNTS)
					break; // We can break because mount spells belong only to one skillline (at least 310 flyers do)

				pSpellInfo = sSpellStore.LookupEntry(itr->first);
				for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
					if (pSpellInfo->EffectApplyAuraName[i]
							== SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED
							&& SpellMgr::CalculateSpellEffectAmount(pSpellInfo,
									i) == 310) {
						SetHas310Flyer(true);
						return true;
					}
			}
		}
	}

	return false;
}

void Player::RemoveSpellCooldown(uint32 spell_id, bool update /* = false */) {
	m_spellCooldowns.erase(spell_id);

	if (update)
		SendClearCooldown(spell_id, this);
}

// I am not sure which one is more efficient
void Player::RemoveCategoryCooldown(uint32 cat) {
	SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(
			cat);
	if (i_scstore != sSpellCategoryStore.end())
		for (SpellCategorySet::const_iterator i_scset =
				i_scstore->second.begin(); i_scset != i_scstore->second.end();
				++i_scset)
			RemoveSpellCooldown(*i_scset, true);
}

void Player::RemoveSpellCategoryCooldown(uint32 cat,
		bool update /* = false */) {
	SpellCategoryStore::const_iterator ct = sSpellCategoryStore.find(cat);
	if (ct == sSpellCategoryStore.end())
		return;

	const SpellCategorySet& ct_set = ct->second;
	for (SpellCooldowns::const_iterator i = m_spellCooldowns.begin();
			i != m_spellCooldowns.end();) {
		if (ct_set.find(i->first) != ct_set.end())
			RemoveSpellCooldown((i++)->first, update);
		else
			++i;
	}
}

void Player::RemoveArenaSpellCooldowns(bool removeActivePetCooldowns) {
	// remove cooldowns on spells that have <= 10 min CD

	SpellCooldowns::iterator itr, next;
	for (itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); itr =
			next) {
		next = itr;
		++next;
		SpellEntry const * entry = sSpellStore.LookupEntry(itr->first);
		// check if spellentry is present and if the cooldown is less or equal to 10 min
		if (entry && entry->RecoveryTime <= 10 * MINUTE * IN_MILLISECONDS
				&& entry->CategoryRecoveryTime <= 10 * MINUTE * IN_MILLISECONDS) {
			// remove & notify
			RemoveSpellCooldown(itr->first, true);
		}
	}

	// pet cooldowns
	if (removeActivePetCooldowns)
		if (Pet *pet = GetPet()) {
			// notify player
			for (CreatureSpellCooldowns::const_iterator itr =
					pet->m_CreatureSpellCooldowns.begin();
					itr != pet->m_CreatureSpellCooldowns.end(); ++itr)
				SendClearCooldown(itr->first, pet);

			// actually clear cooldowns
			pet->m_CreatureSpellCooldowns.clear();
		}
}

void Player::RemoveAllSpellCooldown() {
	if (!m_spellCooldowns.empty()) {
		for (SpellCooldowns::const_iterator itr = m_spellCooldowns.begin();
				itr != m_spellCooldowns.end(); ++itr)
			SendClearCooldown(itr->first, this);

		m_spellCooldowns.clear();
	}
}

void Player::_LoadSpellCooldowns(PreparedQueryResult result) {
	// some cooldowns can be already set at aura loading...

	//QueryResult *result = CharacterDatabase.PQuery("SELECT spell, item, time FROM character_spell_cooldown WHERE guid = '%u'", GetGUIDLow());

	if (result) {
		time_t curTime = time(NULL);

		do {
			Field* fields = result->Fetch();
			uint32 spell_id = fields[0].GetUInt32();
			uint32 item_id = fields[1].GetUInt32();
			time_t db_time = time_t(fields[2].GetUInt32());

			if (!sSpellStore.LookupEntry(spell_id)) {
				sLog->outError(
						"Player %u has unknown spell %u in `character_spell_cooldown`, skipping.",
						GetGUIDLow(), spell_id);
				continue;
			}

			// skip outdated cooldown
			if (db_time <= curTime)
				continue;

			AddSpellCooldown(spell_id, item_id, db_time);

			sLog->outDebug(
					LOG_FILTER_PLAYER_LOADING,
					"Player (GUID: %u) spell %u, item %u cooldown loaded (%u secs).",
					GetGUIDLow(), spell_id, item_id, uint32(db_time - curTime));
		} while (result->NextRow());
	}
}

void Player::_SaveSpellCooldowns(SQLTransaction& trans) {
	trans->PAppend("DELETE FROM character_spell_cooldown WHERE guid = '%u'",
			GetGUIDLow());

	time_t curTime = time(NULL);
	time_t infTime = curTime + infinityCooldownDelayCheck;

	bool first_round = true;
	std::ostringstream ss;

	// remove outdated and save active
	for (SpellCooldowns::iterator itr = m_spellCooldowns.begin();
			itr != m_spellCooldowns.end();) {
		if (itr->second.end <= curTime)
			m_spellCooldowns.erase(itr++);
		else if (itr->second.end <= infTime) // not save locked cooldowns, it will be reset or set at reload
				{
			if (first_round) {
				ss
						<< "INSERT INTO character_spell_cooldown (guid, spell, item, time) VALUES ";
				first_round = false;
			}
			// next new/changed record prefix
			else
				ss << ", ";
			ss << "(" << GetGUIDLow() << ", " << itr->first << ", "
					<< itr->second.itemid << ", " << uint64(itr->second.end)
					<< ")";
			++itr;
		} else
			++itr;
	}
	// if something changed execute
	if (!first_round)
		trans->Append(ss.str().c_str());
}

uint32 Player::resetTalentsCost() const {
	// The first time reset costs 1 gold
	if (m_resetTalentsCost < 1 * GOLD)
		return 1 * GOLD;
	// then 5 gold
	else if (m_resetTalentsCost < 5 * GOLD)
		return 5 * GOLD;
	// After that it increases in increments of 5 gold
	else if (m_resetTalentsCost < 10 * GOLD)
		return 10 * GOLD;
	else {
		uint64 months = (sWorld->GetGameTime() - m_resetTalentsTime) / MONTH;
		if (months > 0) {
			// This cost will be reduced by a rate of 5 gold per month
			int32 new_cost = int32(m_resetTalentsCost - 5 * GOLD * months);
			// to a minimum of 10 gold.
			return (new_cost < 10 * GOLD ? 10 * GOLD : new_cost);
		} else {
			// After that it increases in increments of 5 gold
			int32 new_cost = m_resetTalentsCost + 5 * GOLD;
			// until it hits a cap of 50 gold.
			if (new_cost > 50 * GOLD)
				new_cost = 50 * GOLD;
			return new_cost;
		}
	}
}

bool Player::resetTalents(bool no_cost) {
	sScriptMgr->OnPlayerTalentsReset(this, no_cost);

	// not need after this call
	if (HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
		RemoveAtLoginFlag(AT_LOGIN_RESET_TALENTS, true);

	uint32 talentPointsForLevel = CalculateTalentsPoints();

	if (m_usedTalentCount == 0) {
		SetFreeTalentPoints(talentPointsForLevel);
		return false;
	}

	uint32 cost = 0;

	if (!no_cost && !sWorld->getBoolConfig(CONFIG_NO_RESET_TALENT_COST)) {
		cost = resetTalentsCost();

		if (!HasEnoughMoney(cost)) {
			SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
			return false;
		}
	}

	for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i) {
		TalentEntry const *talentInfo = sTalentStore.LookupEntry(i);

		if (!talentInfo)
			continue;

		TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(
				talentInfo->TalentTab);

		if (!talentTabInfo)
			continue;

		// unlearn only talents for character class
		// some spell learned by one class as normal spells or know at creation but another class learn it as talent,
		// to prevent unexpected lost normal learned spell skip another class talents
		if ((getClassMask() & talentTabInfo->ClassMask) == 0)
			continue;

		for (int8 rank = MAX_TALENT_RANK - 1; rank >= 0; --rank) {
			// skip non-existant talent ranks
			if (talentInfo->RankID[rank] == 0)
				continue;
			removeSpell(talentInfo->RankID[rank], true);
			if (const SpellEntry *_spellEntry = sSpellStore.LookupEntry(talentInfo->RankID[rank]))
				for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) // search through the SpellEntry for valid trigger spells
					if (_spellEntry->EffectTriggerSpell[i] > 0
							&& _spellEntry->Effect[i]
									== SPELL_EFFECT_LEARN_SPELL)
						removeSpell(_spellEntry->EffectTriggerSpell[i], true); // and remove any spells that the talent teaches
			// if this talent rank can be found in the PlayerTalentMap, mark the talent as removed so it gets deleted
			PlayerTalentMap::iterator plrTalent = m_talents[m_activeSpec]->find(
					talentInfo->RankID[rank]);
			if (plrTalent != m_talents[m_activeSpec]->end())
				plrTalent->second->state = PLAYERSPELL_REMOVED;
		}
	}

	RemovePet(NULL, PET_SLOT_ACTUAL_PET_SLOT, true);

	for (uint32 i = 0; i < sTalentTreePrimarySpellsStore.GetNumRows(); ++i) {
		TalentTreePrimarySpellsEntry const *talentInfo =
				sTalentTreePrimarySpellsStore.LookupEntry(i);

		if (!talentInfo
				|| talentInfo->TalentTabID != GetTalentBranchSpec(m_activeSpec))
			continue;

		removeSpell(talentInfo->SpellID, true);
	}

	m_branchSpec[m_activeSpec] = 0;

	SQLTransaction trans = CharacterDatabase.BeginTransaction();
	_SaveTalents(trans);
	_SaveTalentBranchSpecs(trans);
	_SaveSpells(trans);
	CharacterDatabase.CommitTransaction(trans);

	SetFreeTalentPoints(talentPointsForLevel);

	if (!no_cost) {
		ModifyMoney(-(int32) cost);
		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS, cost);
		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS, 1);

		m_resetTalentsCost = cost;
		m_resetTalentsTime = time(NULL);
	}

	/* when prev line will dropped use next line
	 if (Pet* pet = GetPet())
	 {
	 if (pet->getPetType() == HUNTER_PET && !pet->GetCreatureInfo()->isTameable(CanTameExoticPets()))
	 RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
	 }
	 */

	return true;
}

Mail* Player::GetMail(uint32 id) {
	for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
		if ((*itr)->messageID == id)
			return (*itr);

	return NULL;
}

void Player::_SetCreateBits(UpdateMask *updateMask, Player *target) const {
	if (target == this)
		Object::_SetCreateBits(updateMask, target);
	else {
		for (uint16 index = 0; index < PLAYER_END_NOT_SELF; index++)
			if (GetUInt32Value(index) != 0 && updateVisualBits.GetBit(index))
				updateMask->SetBit(index);
	}
}

void Player::_SetUpdateBits(UpdateMask *updateMask, Player *target) const {
	if (target == this)
		Object::_SetUpdateBits(updateMask, target);
	else {
		Object::_SetUpdateBits(updateMask, target);
		*updateMask &= updateVisualBits;
	}
}

void Player::InitVisibleBits() {
	updateVisualBits.SetCount(PLAYER_END);

	updateVisualBits.SetBit(OBJECT_FIELD_GUID);
	updateVisualBits.SetBit(OBJECT_FIELD_TYPE);
	updateVisualBits.SetBit(OBJECT_FIELD_ENTRY);
	updateVisualBits.SetBit(OBJECT_FIELD_DATA + 0);
	updateVisualBits.SetBit(OBJECT_FIELD_DATA + 1);
	updateVisualBits.SetBit(OBJECT_FIELD_SCALE_X);
	updateVisualBits.SetBit(UNIT_FIELD_CHARM + 0);
	updateVisualBits.SetBit(UNIT_FIELD_CHARM + 1);
	updateVisualBits.SetBit(UNIT_FIELD_SUMMON + 0);
	updateVisualBits.SetBit(UNIT_FIELD_SUMMON + 1);
	updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY + 0);
	updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY + 1);
	updateVisualBits.SetBit(UNIT_FIELD_TARGET + 0);
	updateVisualBits.SetBit(UNIT_FIELD_TARGET + 1);
	updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT + 0);
	updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT + 1);
	updateVisualBits.SetBit(UNIT_FIELD_BYTES_0);
	updateVisualBits.SetBit(UNIT_FIELD_HEALTH);
	updateVisualBits.SetBit(UNIT_FIELD_POWER1);
	updateVisualBits.SetBit(UNIT_FIELD_POWER2);
	updateVisualBits.SetBit(UNIT_FIELD_POWER3);
	updateVisualBits.SetBit(UNIT_FIELD_POWER4);
	updateVisualBits.SetBit(UNIT_FIELD_POWER5);
	updateVisualBits.SetBit(UNIT_FIELD_POWER6);
	updateVisualBits.SetBit(UNIT_FIELD_POWER7);
	updateVisualBits.SetBit(UNIT_FIELD_POWER8);
	updateVisualBits.SetBit(UNIT_FIELD_POWER9);
	updateVisualBits.SetBit(UNIT_FIELD_POWER10);
	updateVisualBits.SetBit(UNIT_FIELD_MAXHEALTH);
	updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER1);
	updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER2);
	updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER3);
	updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER4);
	updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER5);
	updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER6);
	updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER7);
	updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER8);
	updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER9);
	updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER10);
	updateVisualBits.SetBit(UNIT_FIELD_LEVEL);
	updateVisualBits.SetBit(UNIT_FIELD_FACTIONTEMPLATE);
	updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID + 0);
	updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID + 1);
	updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_ID + 2);
	updateVisualBits.SetBit(UNIT_FIELD_FLAGS);
	updateVisualBits.SetBit(UNIT_FIELD_FLAGS_2);
	updateVisualBits.SetBit(UNIT_FIELD_AURASTATE);
	updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME + 0);
	updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME + 1);
	updateVisualBits.SetBit(UNIT_FIELD_BOUNDINGRADIUS);
	updateVisualBits.SetBit(UNIT_FIELD_COMBATREACH);
	updateVisualBits.SetBit(UNIT_FIELD_DISPLAYID);
	updateVisualBits.SetBit(UNIT_FIELD_NATIVEDISPLAYID);
	updateVisualBits.SetBit(UNIT_FIELD_MOUNTDISPLAYID);
	updateVisualBits.SetBit(UNIT_FIELD_BYTES_1);
	updateVisualBits.SetBit(UNIT_FIELD_PETNUMBER);
	updateVisualBits.SetBit(UNIT_FIELD_PET_NAME_TIMESTAMP);
	updateVisualBits.SetBit(UNIT_DYNAMIC_FLAGS);
	updateVisualBits.SetBit(UNIT_CHANNEL_SPELL);
	updateVisualBits.SetBit(UNIT_MOD_CAST_SPEED);
	updateVisualBits.SetBit(UNIT_FIELD_BASE_MANA);
	updateVisualBits.SetBit(UNIT_FIELD_BYTES_2);
	updateVisualBits.SetBit(UNIT_FIELD_HOVERHEIGHT);

	updateVisualBits.SetBit(PLAYER_DUEL_ARBITER + 0);
	updateVisualBits.SetBit(PLAYER_DUEL_ARBITER + 1);
	updateVisualBits.SetBit(PLAYER_FLAGS);
	updateVisualBits.SetBit(PLAYER_GUILDRANK);
	//updateVisualBits.SetBit(PLAYER_GUILDDELETE_DATE);
	updateVisualBits.SetBit(PLAYER_GUILDLEVEL);
	updateVisualBits.SetBit(PLAYER_BYTES);
	updateVisualBits.SetBit(PLAYER_BYTES_2);
	updateVisualBits.SetBit(PLAYER_BYTES_3);
	updateVisualBits.SetBit(PLAYER_DUEL_TEAM);
	//updateVisualBits.SetBit(PLAYER_GUILD_TIMESTAMP);
	updateVisualBits.SetBit(UNIT_NPC_FLAGS);

	// PLAYER_QUEST_LOG_x also visible bit on official (but only on party/raid)...
	for (uint16 i = PLAYER_QUEST_LOG_1_1; i < PLAYER_QUEST_LOG_25_2; i +=
			MAX_QUEST_OFFSET)
		updateVisualBits.SetBit(i);

	// Players visible items are not inventory stuff
	for (uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i) {
		uint16 offset = i * 2;

		// item entry
		updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_ENTRYID + offset);
		// enchant
		updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + offset);
	}

	updateVisualBits.SetBit(PLAYER_CHOSEN_TITLE);
}

void Player::BuildCreateUpdateBlockForPlayer(UpdateData *data,
		Player *target) const {
	Unit::BuildCreateUpdateBlockForPlayer(data, target);

	for (uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i) {
		if (m_items[i] == NULL)
			continue;

		m_items[i]->BuildCreateUpdateBlockForPlayer(data, target);
	}

	if (target == this) {
		for (uint8 i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) {
			if (m_items[i] == NULL)
				continue;

			m_items[i]->BuildCreateUpdateBlockForPlayer(data, target);
		}
		for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i) {
			if (m_items[i] == NULL)
				continue;

			m_items[i]->BuildCreateUpdateBlockForPlayer(data, target);
		}
	}
}

void Player::DestroyForPlayer(Player *target, bool anim) const {
	Unit::DestroyForPlayer(target, anim);

	for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i) {
		if (m_items[i] == NULL)
			continue;

		m_items[i]->DestroyForPlayer(target);
	}

	if (target == this) {
		for (uint8 i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i) {
			if (m_items[i] == NULL)
				continue;

			m_items[i]->DestroyForPlayer(target);
		}
		for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i) {
			if (m_items[i] == NULL)
				continue;

			m_items[i]->DestroyForPlayer(target);
		}
	}
}

bool Player::HasSpell(uint32 spell) const {
	PlayerSpellMap::const_iterator itr = m_spells.find(spell);
	return (itr != m_spells.end() && itr->second->state != PLAYERSPELL_REMOVED
			&& !itr->second->disabled);
}

bool Player::HasTalent(uint32 spell, uint8 spec) const {
	PlayerTalentMap::const_iterator itr = m_talents[spec]->find(spell);
	return (itr != m_talents[spec]->end()
			&& itr->second->state != PLAYERSPELL_REMOVED);
}

bool Player::HasActiveSpell(uint32 spell) const {
	PlayerSpellMap::const_iterator itr = m_spells.find(spell);
	return (itr != m_spells.end() && itr->second->state != PLAYERSPELL_REMOVED
			&& itr->second->active && !itr->second->disabled);
}

TrainerSpellState Player::GetTrainerSpellState(
		TrainerSpell const* trainer_spell) const {
	if (!trainer_spell)
		return TRAINER_SPELL_RED;

	bool hasSpell = true;
	for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) {
		if (!trainer_spell->learnedSpell[i])
			continue;

		if (!HasSpell(trainer_spell->learnedSpell[i])) {
			hasSpell = false;
			break;
		}
	}
	// known spell
	if (hasSpell)
		return TRAINER_SPELL_GRAY;

	// check skill requirement
	if (trainer_spell->reqSkill
			&& GetBaseSkillValue(trainer_spell->reqSkill)
					< trainer_spell->reqSkillValue)
		return TRAINER_SPELL_RED;

	// check level requirement
	if (getLevel() < trainer_spell->reqLevel)
		return TRAINER_SPELL_RED;

	for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) {
		if (!trainer_spell->learnedSpell[i])
			continue;

		// check race/class requirement
		if (!IsSpellFitByClassAndRace(trainer_spell->learnedSpell[i]))
			return TRAINER_SPELL_RED;

		if (SpellChainNode const* spell_chain = sSpellMgr->GetSpellChainNode(trainer_spell->learnedSpell[i])) {
			// check prev.rank requirement
			if (spell_chain->prev && !HasSpell(spell_chain->prev))
				return TRAINER_SPELL_RED;
		}

		SpellsRequiringSpellMapBounds spellsRequired =
				sSpellMgr->GetSpellsRequiredForSpellBounds(
						trainer_spell->learnedSpell[i]);
		for (SpellsRequiringSpellMap::const_iterator itr = spellsRequired.first;
				itr != spellsRequired.second; ++itr) {
			// check additional spell requirement
			if (!HasSpell(itr->second))
				return TRAINER_SPELL_RED;
		}
	}

	// check primary prof. limit
	// first rank of primary profession spell when there are no proffesions avalible is disabled
	for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) {
		if (!trainer_spell->learnedSpell[i])
			continue;
		if ((sSpellMgr->IsPrimaryProfessionFirstRankSpell(
				trainer_spell->learnedSpell[i]))
				&& (GetFreePrimaryProfessionPoints() == 0))
			return TRAINER_SPELL_GREEN_DISABLED;
	}

	return TRAINER_SPELL_GREEN;
}

/**
 * Deletes a character from the database
 *
 * The way, how the characters will be deleted is decided based on the config option.
 *
 * @see Player::DeleteOldCharacters
 *
 * @param playerguid       the low-GUID from the player which should be deleted
 * @param accountId        the account id from the player
 * @param updateRealmChars when this flag is set, the amount of characters on that realm will be updated in the realmlist
 * @param deleteFinally    if this flag is set, the config option will be ignored and the character will be permanently removed from the database
 */
void Player::DeleteFromDB(uint64 playerguid, uint32 accountId,
		bool updateRealmChars, bool deleteFinally) {
	// for not existed account avoid update realm
	if (accountId == 0)
		updateRealmChars = false;

	uint32 charDelete_method = sWorld->getIntConfig(CONFIG_CHARDELETE_METHOD);
	uint32 charDelete_minLvl = sWorld->getIntConfig(
			CONFIG_CHARDELETE_MIN_LEVEL);

	// if we want to finally delete the character or the character does not meet the level requirement,
	// we set it to mode CHAR_DELETE_REMOVE
	if (deleteFinally || Player::GetLevelFromDB(playerguid) < charDelete_minLvl)
		charDelete_method = CHAR_DELETE_REMOVE;

	uint32 guid = GUID_LOPART(playerguid);

	// convert corpse to bones if exist (to prevent exiting Corpse in World without DB entry)
	// bones will be deleted by corpse/bones deleting thread shortly
	sObjectAccessor->ConvertCorpseForPlayer(playerguid);

	if (uint32 guildId = GetGuildIdFromDB(playerguid))
		if (Guild * pGuild = sObjectMgr->GetGuildById(guildId))
			pGuild->DeleteMember(guid);

	// remove from arena teams
	LeaveAllArenaTeams(playerguid);

	// the player was uninvited already on logout so just remove from group
	QueryResult resultGroup = CharacterDatabase.PQuery(
			"SELECT guid FROM group_member WHERE memberGuid=%u", guid);
	if (resultGroup)
		if (Group * group = sObjectMgr->GetGroupByGUID(
				(*resultGroup)[0].GetUInt32()))
			RemoveFromGroup(group, playerguid);

	// Remove signs from petitions (also remove petitions if owner);
	RemovePetitionsAndSigns(playerguid, 10);

	switch (charDelete_method) {
	// Completely remove from the database
	case CHAR_DELETE_REMOVE: {
		SQLTransaction trans = CharacterDatabase.BeginTransaction();
		// Return back all mails with COD and Item                 0  1           2              3      4       5          6     7
		QueryResult resultMail =
				CharacterDatabase.PQuery(
						"SELECT id, messageType, mailTemplateId, sender, subject, body, money, has_items FROM mail WHERE receiver='%u' AND has_items<>0 AND cod<>0",
						guid);
		if (resultMail) {
			do {
				Field *fields = resultMail->Fetch();

				uint32 mail_id = fields[0].GetUInt32();
				uint16 mailType = fields[1].GetUInt16();
				uint16 mailTemplateId = fields[2].GetUInt16();
				uint32 sender = fields[3].GetUInt32();
				std::string subject = fields[4].GetString();
				std::string body = fields[5].GetString();
				uint32 money = fields[6].GetUInt32();
				bool has_items = fields[7].GetBool();

				// We can return mail now
				// So firstly delete the old one
				trans->PAppend("DELETE FROM mail WHERE id = '%u'", mail_id);

				// Mail is not from player
				if (mailType != MAIL_NORMAL) {
					if (has_items)
						trans->PAppend(
								"DELETE FROM mail_items WHERE mail_id = '%u'",
								mail_id);
					continue;
				}

				MailDraft draft(subject, body);
				if (mailTemplateId)
					draft = MailDraft(mailTemplateId, false); // items are already included

				if (has_items) {
					// Data needs to be at first place for Item::LoadFromDB
					PreparedStatement* stmt =
							CharacterDatabase.GetPreparedStatement(
									CHAR_LOAD_PLAYER_MAILITEMS);
					stmt->setUInt32(0, mail_id);
					PreparedQueryResult resultItems = CharacterDatabase.Query(
							stmt);
					if (resultItems) {
						do {
							Field* fields = resultItems->Fetch();
							uint32 item_guidlow = fields[11].GetUInt32();
							uint32 item_template = fields[12].GetUInt32();

							ItemPrototype const* itemProto =
									ObjectMgr::GetItemPrototype(item_template);
							if (!itemProto) {
								PreparedStatement* stmt =
										CharacterDatabase.GetPreparedStatement(
												CHAR_DEL_ITEM_INSTANCE);
								stmt->setUInt32(0, item_guidlow);
								trans->Append(stmt);
								continue;
							}

							Item *pItem = NewItemOrBag(itemProto);
							if (!pItem->LoadFromDB(item_guidlow,
									MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER),
									fields, item_template)) {
								pItem->FSetState(ITEM_REMOVED);
								pItem->SaveToDB(trans); // it also deletes item object!
								continue;
							}

							draft.AddItem(pItem);
						} while (resultItems->NextRow());
					}
				}

				trans->PAppend("DELETE FROM mail_items WHERE mail_id = '%u'",
						mail_id);

				uint32 pl_account = sObjectMgr->GetPlayerAccountIdByGUID(
						MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));

				draft.AddMoney(money).SendReturnToSender(pl_account, guid,
						sender, trans);
			} while (resultMail->NextRow());
		}

		// Unsummon and delete for pets in world is not required: player deleted from CLI or character list with not loaded pet.
		// NOW we can finally clear other DB data related to character
		if (QueryResult resultPets = CharacterDatabase.PQuery("SELECT id FROM character_pet WHERE owner = '%u'", guid)) {
			do {
				uint32 petguidlow = (*resultPets)[0].GetUInt32();
				Pet::DeleteFromDB(petguidlow);
			} while (resultPets->NextRow());
		}

		// Delete char from social list of online chars
		if (QueryResult resultFriends = CharacterDatabase.PQuery("SELECT DISTINCT guid FROM character_social WHERE friend = '%u'", guid)) {
			do {
				if (Player * pFriend =
						sObjectAccessor->FindPlayer(
								MAKE_NEW_GUID((*resultFriends)[0].GetUInt32(), 0, HIGHGUID_PLAYER))) {
					if (pFriend->IsInWorld()) {
						pFriend->GetSocial()->RemoveFromSocialList(guid, false);
						sSocialMgr->SendFriendStatus(pFriend, FRIEND_REMOVED,
								guid, false);
					}
				}
			} while (resultFriends->NextRow());
		}

		trans->PAppend("DELETE FROM characters WHERE guid = '%u'", guid);
		trans->PAppend("DELETE FROM character_account_data WHERE guid = '%u'",
				guid);
		trans->PAppend("DELETE FROM character_declinedname WHERE guid = '%u'",
				guid);
		trans->PAppend("DELETE FROM character_action WHERE guid = '%u'", guid);
		trans->PAppend("DELETE FROM character_aura WHERE guid = '%u'", guid);
		trans->PAppend("DELETE FROM character_gifts WHERE guid = '%u'", guid);
		trans->PAppend("DELETE FROM character_homebind WHERE guid = '%u'",
				guid);
		trans->PAppend("DELETE FROM character_instance WHERE guid = '%u'",
				guid);
		trans->PAppend("DELETE FROM character_inventory WHERE guid = '%u'",
				guid);
		trans->PAppend("DELETE FROM character_queststatus WHERE guid = '%u'",
				guid);
		trans->PAppend(
				"DELETE FROM character_queststatus_rewarded WHERE guid = '%u'",
				guid);
		trans->PAppend("DELETE FROM character_reputation WHERE guid = '%u'",
				guid);
		trans->PAppend("DELETE FROM character_spell WHERE guid = '%u'", guid);
		trans->PAppend("DELETE FROM character_spell_cooldown WHERE guid = '%u'",
				guid);
		trans->PAppend("DELETE FROM gm_tickets WHERE playerGuid = '%u'", guid);
		trans->PAppend("DELETE FROM item_instance WHERE owner_guid = '%u'",
				guid);
		trans->PAppend(
				"DELETE FROM character_social WHERE guid = '%u' OR friend='%u'",
				guid, guid);
		trans->PAppend("DELETE FROM mail WHERE receiver = '%u'", guid);
		trans->PAppend("DELETE FROM mail_items WHERE receiver = '%u'", guid);
		trans->PAppend("DELETE FROM character_pet WHERE owner = '%u'", guid);
		trans->PAppend(
				"DELETE FROM character_pet_declinedname WHERE owner = '%u'",
				guid);
		trans->PAppend("DELETE FROM character_achievement WHERE guid = '%u' " // NOTE: These achievements have flags & 256 in DBC.
						"AND achievement NOT BETWEEN '456' AND '467' "// Realm First Level 80
						"AND achievement NOT BETWEEN '1400' AND '1427' "// Realm First Raid Achievements
						"AND achievement NOT IN(1463, 3117, 3259) ", guid); // Realm First Northen Vanguard + Raid Achievements
		trans->PAppend(
				"DELETE FROM character_achievement_progress WHERE guid = '%u'",
				guid);
		trans->PAppend("DELETE FROM character_equipmentsets WHERE guid = '%u'",
				guid);
		trans->PAppend(
				"DELETE FROM guild_eventlog WHERE PlayerGuid1 = '%u' OR PlayerGuid2 = '%u'",
				guid, guid);
		trans->PAppend(
				"DELETE FROM guild_bank_eventlog WHERE PlayerGuid = '%u'",
				guid);
		PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(
				CHAR_DEL_PLAYER_BGDATA);
		stmt->setUInt32(0, guid);
		trans->Append(stmt);
		trans->PAppend("DELETE FROM character_glyphs WHERE guid = '%u'", guid);
		trans->PAppend(
				"DELETE FROM character_queststatus_daily WHERE guid = '%u'",
				guid);
		trans->PAppend("DELETE FROM character_talent WHERE guid = '%u'", guid);
		trans->PAppend("DELETE FROM character_skills WHERE guid = '%u'", guid);

		CharacterDatabase.CommitTransaction(trans);
		break;
	}
		// The character gets unlinked from the account, the name gets freed up and appears as deleted ingame
	case CHAR_DELETE_UNLINK:
		CharacterDatabase.PExecute(
				"UPDATE characters SET deleteInfos_Name=name, deleteInfos_Account=account, deleteDate='" UI64FMTD "', name='', account=0 WHERE guid=%u",
				uint64(time(NULL)), guid);
		break;
	default:
		sLog->outError("Player::DeleteFromDB: Unsupported delete method: %u.",
				charDelete_method);
	}

	if (updateRealmChars)
		sWorld->UpdateRealmCharCount(accountId);
}

/**
 * Characters which were kept back in the database after being deleted and are now too old (see config option "CharDelete.KeepDays"), will be completely deleted.
 *
 * @see Player::DeleteFromDB
 */
void Player::DeleteOldCharacters() {
	uint32 keepDays = sWorld->getIntConfig(CONFIG_CHARDELETE_KEEP_DAYS);
	if (!keepDays)
		return;

	Player::DeleteOldCharacters(keepDays);
}

/**
 * Characters which were kept back in the database after being deleted and are older than the specified amount of days, will be completely deleted.
 *
 * @see Player::DeleteFromDB
 *
 * @param keepDays overrite the config option by another amount of days
 */
void Player::DeleteOldCharacters(uint32 keepDays) {
	sLog->outString(
			"Player::DeleteOldChars: Deleting all characters which have been deleted %u days before...",
			keepDays);

	QueryResult resultChars =
			CharacterDatabase.PQuery(
					"SELECT guid, deleteInfos_Account FROM characters WHERE deleteDate IS NOT NULL AND deleteDate < '%u'",
					uint32(time(NULL) - time_t(keepDays * DAY)));
	if (resultChars) {
		sLog->outString(
				"Player::DeleteOldChars: Found " UI64FMTD " character(s) to delete",
				resultChars->GetRowCount());
		do {
			Field *charFields = resultChars->Fetch();
			Player::DeleteFromDB(charFields[0].GetUInt64(),
					charFields[1].GetUInt32(), true, true);
		} while (resultChars->NextRow());
	}
}

void Player::SetMovement(PlayerMovementType pType) {
	//sLog->outError("void Player::SetMovement(PlayerMovementType pType)");
	WorldPacket data;

	switch (pType) {
	case MOVE_ROOT: {
		//sLog->outError("MOVE ROOT");
		data.Initialize(SMSG_FORCE_MOVE_ROOT, GetPackGUID().size());
		break;
		break;
	}
	case MOVE_UNROOT: {
		//sLog->outError("MOVE UNROOT");
		data.Initialize(SMSG_FORCE_MOVE_UNROOT, GetPackGUID().size());
		break;
		break;
	}
	case MOVE_WATER_WALK: {
		//sLog->outError("MOVE WATER WALK");
		WorldPacket movewaterwalk(SMSG_MULTIPLE_PACKETS, 14);
		movewaterwalk << uint16(SMSG_SPLINE_MOVE_WATER_WALK);
		movewaterwalk.append(GetPackGUID());
		SendMessageToSet(&movewaterwalk, true);
		break;
	}
	case MOVE_LAND_WALK: {
		// sLog->outError("MOVE LAND WALK");
		data.Initialize(SMSG_SPLINE_MOVE_LAND_WALK, GetPackGUID().size());
		break;
		break;
	}
	default: {
		sLog->outError(
				"Player::SetMovement: Unsupported move type (%d), data not sent to client.",
				pType);
		return;
	}
	}
	data.append(GetPackGUID());
	GetSession()->SendPacket(&data);
}

/* Preconditions:
 - a resurrectable corpse must not be loaded for the player (only bones)
 - the player must be in world
 */
void Player::BuildPlayerRepop() {
	WorldPacket data(SMSG_PRE_RESURRECT, GetPackGUID().size());
	data.append(GetPackGUID());
	GetSession()->SendPacket(&data);

	if (getRace() == RACE_NIGHTELF)
		CastSpell(this, 20584, true);
	CastSpell(this, 8326, true);

	// there must be SMSG.FORCE_RUN_SPEED_CHANGE, SMSG.FORCE_SWIM_SPEED_CHANGE, SMSG.MOVE_WATER_WALK
	// there must be SMSG.STOP_MIRROR_TIMER
	// there we must send 888 opcode

	// the player cannot have a corpse already, only bones which are not returned by GetCorpse
	if (GetCorpse()) {
		sLog->outError("BuildPlayerRepop: player %s(%d) already has a corpse",
				GetName(), GetGUIDLow());
		return;
	}

	// create a corpse and place it at the player's location
	CreateCorpse();
	Corpse *corpse = GetCorpse();
	if (!corpse) {
		sLog->outError("Error creating corpse for Player %s [%u]", GetName(),
				GetGUIDLow());
		return;
	}
	GetMap()->Add(corpse);

	// convert player body to ghost
	SetHealth(1);

	SetMovement(MOVE_WATER_WALK);
	if (!GetSession()->isLogingOut())
		SetMovement(MOVE_UNROOT);

	// BG - remove insignia related
	RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);

//    SendCorpseReclaimDelay();

	// to prevent cheating
	corpse->ResetGhostTime();

	StopMirrorTimers(); //disable timers(bars)

	SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, (float) 1.0); //see radius of death player?

	// set and clear other
	SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND);
}

void Player::ResurrectPlayer(float restore_percent, bool applySickness) {
	WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4 * 4); // remove spirit healer position
	data << uint32(-1);
	data << float(0);
	data << float(0);
	data << float(0);
	GetSession()->SendPacket(&data);

	// speed change, land walk

	// remove death flag + set aura
	SetByteValue(UNIT_FIELD_BYTES_1, 3, 0x00);
	if (getRace() == RACE_NIGHTELF)
		RemoveAurasDueToSpell(20584); // speed bonuses
	RemoveAurasDueToSpell(8326); // SPELL_AURA_GHOST

	setDeathState(ALIVE);

	SetMovement(MOVE_LAND_WALK);
	SetMovement(MOVE_UNROOT);

	m_deathTimer = 0;

	// set health/powers (0- will be set in caller)
	if (restore_percent > 0.0f) {
		SetHealth(uint32(GetMaxHealth() * restore_percent));
		SetPower(POWER_MANA, uint32(GetMaxPower(POWER_MANA) * restore_percent));
		SetPower(POWER_RAGE, 0);
		SetPower(POWER_ENERGY,
				uint32(GetMaxPower(POWER_ENERGY) * restore_percent));
	}

	// trigger update zone for alive state zone updates
	uint32 newzone, newarea;
	GetZoneAndAreaId(newzone, newarea);
	UpdateZone(newzone, newarea);
	sOutdoorPvPMgr->HandlePlayerResurrects(this, newzone);

	if (InBattleground()) {
		if (Battleground* bg = GetBattleground())
			bg->HandlePlayerResurrect(this);
	}

	// update visibility
	UpdateObjectVisibility();

	if (!applySickness)
		return;

	//Characters from level 1-10 are not affected by resurrection sickness.
	//Characters from level 11-19 will suffer from one minute of sickness
	//for each level they are above 10.
	//Characters level 20 and up suffer from ten minutes of sickness.
	int32 startLevel = sWorld->getIntConfig(CONFIG_DEATH_SICKNESS_LEVEL);

	if (int32(getLevel()) >= startLevel) {
		// set resurrection sickness
		CastSpell(this, 15007, true);

		// not full duration
		if (int32(getLevel()) < startLevel + 9) {
			int32 delta = (int32(getLevel()) - startLevel + 1) * MINUTE;

			if (Aura * aur = GetAura(15007, GetGUID())) {
				aur->SetDuration(delta * IN_MILLISECONDS);
			}
		}
	}
}

/**
 * FallMode = 0 implies that the player is dying, or already dead, and the proper death state will be set.
 *          = 1 simply causes the player to plummet towards the ground, and not suffer any damage.
 *          = 2 causes the player to plummet towards the ground, and causes falling damage, regardless
 *              of any auras that might of prevented fall damage.
 */
bool Player::FallGround(uint8 FallMode) {
	// Let's abort after we called this function one time
	if (getDeathState() == DEAD_FALLING && FallMode == 0)
		return false;

	float x, y, z;
	GetPosition(x, y, z);
	float ground_Z = GetMap()->GetHeight(x, y, z);
	float z_diff = 0.0f;
	if ((z_diff = fabs(ground_Z - z)) < 0.1f)
		return false;

	GetMotionMaster()->MoveFall(ground_Z, EVENT_FALL_GROUND);

	// Below formula for falling damage is from Player::HandleFall
	if (FallMode == 2 && z_diff >= 14.57f) {
		uint32 damage = std::min(
				GetMaxHealth(),
				(uint32) ((0.018f * z_diff - 0.2426f) * GetMaxHealth()
						* sWorld->getRate(RATE_DAMAGE_FALL)));
		if (damage > 0)
			EnvironmentalDamage(DAMAGE_FALL, damage);
	} else if (FallMode == 0)
		Unit::setDeathState(DEAD_FALLING);
	return true;
}

void Player::KillPlayer() {
	if (IsFlying() && !GetTransport())
		FallGround();

	SetMovement(MOVE_ROOT);

	StopMirrorTimers(); //disable timers(bars)

	setDeathState(CORPSE);
	//SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_IN_PVP);

	SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NONE);
	ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER,
			!sMapStore.LookupEntry(GetMapId())->Instanceable());

	// 6 minutes until repop at graveyard
	m_deathTimer = 6 * MINUTE * IN_MILLISECONDS;

	UpdateCorpseReclaimDelay(); // dependent at use SetDeathPvP() call before kill
	SendCorpseReclaimDelay();

	// don't create corpse at this moment, player might be falling

	// update visibility
	UpdateObjectVisibility();
}

void Player::CreateCorpse() {
	// prevent existence 2 corpse for player
	SpawnCorpseBones();

	uint32 _uf, _pb, _pb2, _cfb1, _cfb2;

	Corpse *corpse = new Corpse(
			(m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) ?
					CORPSE_RESURRECTABLE_PVP : CORPSE_RESURRECTABLE_PVE);
	SetPvPDeath(false);

	if (!corpse->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_CORPSE), this))
	{
		delete corpse;
		return;
	}

	_uf = GetUInt32Value(UNIT_FIELD_BYTES_0);
	_pb = GetUInt32Value(PLAYER_BYTES);
	_pb2 = GetUInt32Value(PLAYER_BYTES_2);

	uint8 race = (uint8) (_uf);
	uint8 skin = (uint8) (_pb);
	uint8 face = (uint8) (_pb >> 8);
	uint8 hairstyle = (uint8) (_pb >> 16);
	uint8 haircolor = (uint8) (_pb >> 24);
	uint8 facialhair = (uint8) (_pb2);

	_cfb1 = ((0x00) | (race << 8) | (getGender() << 16) | (skin << 24));
	_cfb2 =
			((face) | (hairstyle << 8) | (haircolor << 16) | (facialhair << 24));

	corpse->SetUInt32Value(CORPSE_FIELD_BYTES_1, _cfb1);
	corpse->SetUInt32Value(CORPSE_FIELD_BYTES_2, _cfb2);

	uint32 flags = CORPSE_FLAG_UNK2;
	if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
		flags |= CORPSE_FLAG_HIDE_HELM;
	if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
		flags |= CORPSE_FLAG_HIDE_CLOAK;
	if (InBattleground() && !InArena())
		flags |= CORPSE_FLAG_LOOTABLE; // to be able to remove insignia
	corpse->SetUInt32Value(CORPSE_FIELD_FLAGS, flags);

	corpse->SetUInt32Value(CORPSE_FIELD_DISPLAY_ID, GetNativeDisplayId());

	//corpse->SetUInt32Value(CORPSE_FIELD_GUILD, GetGuildId());

	uint32 iDisplayID;
	uint32 iIventoryType;
	uint32 _cfi;
	for (uint8 i = 0; i < EQUIPMENT_SLOT_END; i++) {
		if (m_items[i]) {
			iDisplayID = m_items[i]->GetProto()->DisplayInfoID;
			iIventoryType = m_items[i]->GetProto()->InventoryType;

			_cfi = iDisplayID | (iIventoryType << 24);
			corpse->SetUInt32Value(CORPSE_FIELD_ITEM + i, _cfi);
		}
	}

	// we do not need to save corpses for BG/arenas
	if (!GetMap()->IsBattlegroundOrArena())
		corpse->SaveToDB();

	// register for player, but not show
	sObjectAccessor->AddCorpse(corpse);
}

void Player::SpawnCorpseBones() {
	if (sObjectAccessor->ConvertCorpseForPlayer(GetGUID()))
		if (!GetSession()->PlayerLogoutWithSave()) // at logout we will already store the player
			SaveToDB(); // prevent loading as ghost without corpse
}

Corpse* Player::GetCorpse() const {
	return sObjectAccessor->GetCorpseForPlayerGUID(GetGUID());
}

void Player::DurabilityLossAll(double percent, bool inventory) {
	for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
		if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			DurabilityLoss(pItem, percent);

	if (inventory) {
		// bags not have durability
		// for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)

		for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END;
				i++)
			if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
				DurabilityLoss(pItem, percent);

		// keys not have durability
		//for (int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)

		for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END;
				i++)
			if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
				for (uint32 j = 0; j < pBag->GetBagSize(); j++)
					if (Item* pItem = GetItemByPos(i, j))
						DurabilityLoss(pItem, percent);
	}
}

void Player::DurabilityLoss(Item* item, double percent) {
	if (!item)
		return;

	uint32 pMaxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);

	if (!pMaxDurability)
		return;
		
    percent /= GetTotalAuraMultiplier(SPELL_AURA_MOD_DURABILITY_LOSS);		

	uint32 pDurabilityLoss = uint32(pMaxDurability * percent);

	if (pDurabilityLoss < 1)
		pDurabilityLoss = 1;

	DurabilityPointsLoss(item, pDurabilityLoss);
}

void Player::DurabilityPointsLossAll(int32 points, bool inventory) {
	for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
		if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			DurabilityPointsLoss(pItem, points);

	if (inventory) {
		// bags not have durability
		// for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)

		for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END;
				i++)
			if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
				DurabilityPointsLoss(pItem, points);

		// keys not have durability
		//for (int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)

		for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END;
				i++)
			if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
				for (uint32 j = 0; j < pBag->GetBagSize(); j++)
					if (Item* pItem = GetItemByPos(i, j))
						DurabilityPointsLoss(pItem, points);
	}
}

void Player::DurabilityPointsLoss(Item* item, int32 points) {
	int32 pMaxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
	int32 pOldDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
	int32 pNewDurability = pOldDurability - points;

	if (pNewDurability < 0)
		pNewDurability = 0;
	else if (pNewDurability > pMaxDurability)
		pNewDurability = pMaxDurability;

	if (pOldDurability != pNewDurability) {
		// modify item stats _before_ Durability set to 0 to pass _ApplyItemMods internal check
		if (pNewDurability == 0 && pOldDurability > 0 && item->IsEquipped())
			_ApplyItemMods(item, item->GetSlot(), false);

		item->SetUInt32Value(ITEM_FIELD_DURABILITY, pNewDurability);

		// modify item stats _after_ restore durability to pass _ApplyItemMods internal check
		if (pNewDurability > 0 && pOldDurability == 0 && item->IsEquipped())
			_ApplyItemMods(item, item->GetSlot(), true);

		item->SetState(ITEM_CHANGED, this);
	}
}

void Player::DurabilityPointLossForEquipSlot(EquipmentSlots slot) {
	if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
		DurabilityPointsLoss(pItem, 1);
}

uint32 Player::DurabilityRepairAll(bool cost, float discountMod,
		bool guildBank) {
	uint32 TotalCost = 0;
	// equipped, backpack, bags itself
	for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
		TotalCost += DurabilityRepair(((INVENTORY_SLOT_BAG_0 << 8) | i), cost,
				discountMod, guildBank);

	// bank, buyback and keys not repaired

	// items in inventory bags
	for (uint8 j = INVENTORY_SLOT_BAG_START; j < INVENTORY_SLOT_BAG_END; j++)
		for (uint8 i = 0; i < MAX_BAG_SIZE; i++)
			TotalCost += DurabilityRepair(((j << 8) | i), cost, discountMod,
					guildBank);
	return TotalCost;
}

uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod,
		bool guildBank) {
	Item* item = GetItemByPos(pos);

	uint32 TotalCost = 0;
	if (!item)
		return TotalCost;

	uint32 maxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
	if (!maxDurability)
		return TotalCost;

	uint32 curDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);

	if (cost) {
		uint32 LostDurability = maxDurability - curDurability;
		if (LostDurability > 0) {
			ItemPrototype const *ditemProto = item->GetProto();

			DurabilityCostsEntry const *dcost =
					sDurabilityCostsStore.LookupEntry(ditemProto->ItemLevel);
			if (!dcost) {
				sLog->outError("RepairDurability: Wrong item lvl %u",
						ditemProto->ItemLevel);
				return TotalCost;
			}

			uint32 dQualitymodEntryId = (ditemProto->Quality + 1) * 2;
			DurabilityQualityEntry const *dQualitymodEntry =
					sDurabilityQualityStore.LookupEntry(dQualitymodEntryId);
			if (!dQualitymodEntry) {
				sLog->outError("RepairDurability: Wrong dQualityModEntry %u",
						dQualitymodEntryId);
				return TotalCost;
			}

			uint32 dmultiplier =
					dcost->multiplier[ItemSubClassToDurabilityMultiplierId(
							ditemProto->Class, ditemProto->SubClass)];
			uint32 costs = uint32(
					LostDurability * dmultiplier
							* double(dQualitymodEntry->quality_mod));

			costs = uint32(
					costs * discountMod * sWorld->getRate(RATE_REPAIRCOST));

			if (costs == 0) //fix for ITEM_QUALITY_ARTIFACT
				costs = 1;

			if (guildBank) {
				if (GetGuildId() == 0) {
					sLog->outStaticDebug("You are not member of a guild");
					return TotalCost;
				}

				Guild *pGuild = sObjectMgr->GetGuildById(GetGuildId());
				if (!pGuild)
					return TotalCost;

				if (!pGuild->HandleMemberWithdrawMoney(GetSession(), costs,
						true))
					return TotalCost;

				TotalCost = costs;
			} else if (!HasEnoughMoney(costs)) {
				sLog->outStaticDebug("You do not have enough money");
				return TotalCost;
			} else
				ModifyMoney(-int32(costs));
		}
	}

	item->SetUInt32Value(ITEM_FIELD_DURABILITY, maxDurability);
	item->SetState(ITEM_CHANGED, this);

	// reapply mods for total broken and repaired item if equipped
	if (IsEquipmentPos(pos) && !curDurability)
		_ApplyItemMods(item, pos & 255, true);
	return TotalCost;
}

void Player::RepopAtGraveyard() {
	// note: this can be called also when the player is alive
	// for example from WorldSession::HandleMovementOpcodes

	AreaTableEntry const *zone = GetAreaEntryByAreaID(GetAreaId());

	// Such zones are considered unreachable as a ghost and the player must be automatically revived
	if ((!isAlive() && zone && zone->flags & AREA_FLAG_NEED_FLY)
			|| GetTransport() || GetPositionZ() < -500.0f) {
		ResurrectPlayer(0.5f);
		SpawnCorpseBones();
	}

	WorldSafeLocsEntry const *ClosestGrave = NULL;

	// Special handle for battleground maps
	if (Battleground *bg = GetBattleground())
		ClosestGrave = bg->GetClosestGraveYard(this);
	else
		ClosestGrave = sObjectMgr->GetClosestGraveYard(GetPositionX(),
				GetPositionY(), GetPositionZ(), GetMapId(), GetTeam());

	// stop countdown until repop
	m_deathTimer = 0;

	// if no grave found, stay at the current location
	// and don't show spirit healer location
	if (ClosestGrave) {
		TeleportTo(ClosestGrave->map_id, ClosestGrave->x, ClosestGrave->y,
				ClosestGrave->z, GetOrientation());
		if (isDead()) // not send if alive, because it used in TeleportTo()
		{
			WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4 * 4); // show spirit healer position on minimap
			data << ClosestGrave->map_id;
			data << ClosestGrave->x;
			data << ClosestGrave->y;
			data << ClosestGrave->z;
			GetSession()->SendPacket(&data);
		}
	} else if (GetPositionZ() < -500.0f)
		TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ,
				GetOrientation());
}

bool Player::CanJoinConstantChannelInZone(ChatChannelsEntry const* channel,
		AreaTableEntry const* zone) {
	if (channel->flags & CHANNEL_DBC_FLAG_ZONE_DEP) {
		if (zone->flags & AREA_FLAG_ARENA_INSTANCE)
			return false;

		if ((channel->flags & CHANNEL_DBC_FLAG_CITY_ONLY)
				&& !(zone->flags & AREA_FLAG_CAPITAL))
			return false;
	}

	return true;
}

void Player::JoinedChannel(Channel *c) {
	m_channels.push_back(c);
}

void Player::LeftChannel(Channel *c) {
	m_channels.remove(c);
}

void Player::CleanupChannels() {
	while (!m_channels.empty()) {
		Channel* ch = *m_channels.begin();
		m_channels.erase(m_channels.begin()); // remove from player's channel list
		ch->Leave(GetGUID(), false); // not send to client, not remove from player's channel list
		if (ChannelMgr* cMgr = channelMgr(GetTeam()))
			cMgr->LeftChannel(ch->GetName()); // deleted channel if empty
	}
	sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Player: channels cleaned up!");
}

void Player::UpdateLocalChannels(uint32 newZone) {
	if (GetSession()->PlayerLoading() && !IsBeingTeleportedFar())
		return; // The client handles it automatically after loading, but not after teleporting

	AreaTableEntry const* current_zone = GetAreaEntryByAreaID(newZone);
	if (!current_zone)
		return;

	ChannelMgr* cMgr = channelMgr(GetTeam());
	if (!cMgr)
		return;

	std::string current_zone_name = current_zone->area_name;

	for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) {
		if (ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(i)) {
			if (!(channel->flags & CHANNEL_DBC_FLAG_ZONE_DEP))
				continue; // Not zone dependent, don't handle it here

			if ((channel->flags & CHANNEL_DBC_FLAG_GUILD_REQ) && GetGuildId())
				continue; // Should not join to these channels automatically

			Channel* usedChannel = NULL;

			for (JoinedChannelsList::iterator itr = m_channels.begin();
					itr != m_channels.end(); ++itr) {
				if ((*itr)->GetChannelId() == i) {
					usedChannel = *itr;
					break;
				}
			}

			Channel* removeChannel = NULL;
			Channel* joinChannel = NULL;
			bool sendRemove = true;

			if (CanJoinConstantChannelInZone(channel, current_zone)) {
				if (!(channel->flags & CHANNEL_DBC_FLAG_GLOBAL)) {
					if (channel->flags & CHANNEL_DBC_FLAG_CITY_ONLY
							&& usedChannel)
						continue; // Already on the channel, as city channel names are not changing

					char new_channel_name_buf[100];
					char const* currentNameExt;

					if (channel->flags & CHANNEL_DBC_FLAG_CITY_ONLY)
						currentNameExt =
								sObjectMgr->GetArkCoreStringForDBCLocale(
										LANG_CHANNEL_CITY);
					else
						currentNameExt = current_zone_name.c_str();

					snprintf(new_channel_name_buf, 100, channel->pattern,
							currentNameExt);

					joinChannel = cMgr->GetJoinChannel(new_channel_name_buf,
							channel->ChannelID);
					if (usedChannel) {
						if (joinChannel != usedChannel) {
							removeChannel = usedChannel;
							sendRemove = false; // Do not send leave channel, it already replaced at client
						} else
							joinChannel = NULL;
					}
				} else
					joinChannel = cMgr->GetJoinChannel(channel->pattern,
							channel->ChannelID);
			} else
				removeChannel = usedChannel;

			if (joinChannel)
				joinChannel->Join(GetGUID(), ""); // Changed Channel: ... or Joined Channel: ...

			if (removeChannel) {
				removeChannel->Leave(GetGUID(), sendRemove); // Leave old channel
				std::string name = removeChannel->GetName(); // Store name, (*i)erase in LeftChannel
				LeftChannel(removeChannel); // Remove from player's channel list
				cMgr->LeftChannel(name); // Delete if empty
			}
		}
	}
}

void Player::LeaveLFGChannel() {
	for (JoinedChannelsList::iterator i = m_channels.begin();
			i != m_channels.end(); ++i) {
		if ((*i)->IsLFG()) {
			(*i)->Leave(GetGUID());
			break;
		}
	}
}

void Player::UpdateDefense() {
	uint32 defense_skill_gain = sWorld->getIntConfig(CONFIG_SKILL_GAIN_DEFENSE);

	if (UpdateSkill(SKILL_DEFENSE, defense_skill_gain)) {
		// update dependent from defense skill part
		UpdateDefenseBonusesMod();
	}
}

void Player::HandleBaseModValue(BaseModGroup modGroup, BaseModType modType,
		float amount, bool apply) {
	if (modGroup >= BASEMOD_END || modType >= MOD_END) {
		sLog->outError(
				"ERROR in HandleBaseModValue(): non existed BaseModGroup of wrong BaseModType!");
		return;
	}

	float val = 1.0f;

	switch (modType) {
	case FLAT_MOD:
		m_auraBaseMod[modGroup][modType] += apply ? amount : -amount;
		break;
	case PCT_MOD:
		if (amount <= -100.0f)
			amount = -200.0f;

		val = (100.0f + amount) / 100.0f;
		m_auraBaseMod[modGroup][modType] *= apply ? val : (1.0f / val);
		break;
	}

	if (!CanModifyStats())
		return;

	switch (modGroup) {
	case CRIT_PERCENTAGE:
		UpdateCritPercentage(BASE_ATTACK);
		break;
	case RANGED_CRIT_PERCENTAGE:
		UpdateCritPercentage(RANGED_ATTACK);
		break;
	case OFFHAND_CRIT_PERCENTAGE:
		UpdateCritPercentage(OFF_ATTACK);
		break;
	case SHIELD_BLOCK_VALUE:
		UpdateShieldBlockValue();
		break;
	default:
		break;
	}
}

float Player::GetBaseModValue(BaseModGroup modGroup,
		BaseModType modType) const {
	if (modGroup >= BASEMOD_END || modType > MOD_END) {
		sLog->outError(
				"trial to access non existed BaseModGroup or wrong BaseModType!");
		return 0.0f;
	}

	if (modType == PCT_MOD && m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f)
		return 0.0f;

	return m_auraBaseMod[modGroup][modType];
}

float Player::GetTotalBaseModValue(BaseModGroup modGroup) const {
	if (modGroup >= BASEMOD_END) {
		sLog->outError("wrong BaseModGroup in GetTotalBaseModValue()!");
		return 0.0f;
	}

	if (m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f)
		return 0.0f;

	return m_auraBaseMod[modGroup][FLAT_MOD] * m_auraBaseMod[modGroup][PCT_MOD];
}

uint32 Player::GetShieldBlockValue() const {
	float value = (m_auraBaseMod[SHIELD_BLOCK_VALUE][FLAT_MOD]
			+ GetStat(STAT_STRENGTH) * 0.5f - 10)
			* m_auraBaseMod[SHIELD_BLOCK_VALUE][PCT_MOD];

	value = (value < 0) ? 0 : value;

	return uint32(value);
}

float Player::GetMeleeCritFromAgility() {
	uint8 level = getLevel();
	uint32 pclass = getClass();

	if (level > GT_MAX_LEVEL)
		level = GT_MAX_LEVEL;

	GtChanceToMeleeCritBaseEntry const *critBase =
			sGtChanceToMeleeCritBaseStore.LookupEntry(pclass - 1);
	GtChanceToMeleeCritEntry const *critRatio =
			sGtChanceToMeleeCritStore.LookupEntry(
					(pclass - 1) * GT_MAX_LEVEL + level - 1);
	if (critBase == NULL || critRatio == NULL)
		return 0.0f;

	float crit = critBase->base + GetStat(STAT_AGILITY) * critRatio->ratio;
	return crit * 100.0f;
}

float Player::GetDodgeFromAgility() {
	// Table for base dodge values
	float dodge_base[MAX_CLASSES] = { 0.0075f, // Warrior
			0.00652f, // Paladin
			-0.0545f, // Hunter
			-0.0059f, // Rogue
			0.03183f, // Priest
			0.0114f, // DK
			0.0167f, // Shaman
			0.034575f, // Mage
			0.02011f, // Warlock
			0.0f, // ??
			-0.0187f // Druid
			};
	// Crit/agility to dodge/agility coefficient multipliers
	float crit_to_dodge[MAX_CLASSES] = { 1.1f, // Warrior
			1.0f, // Paladin
			1.6f, // Hunter
			2.0f, // Rogue
			1.0f, // Priest
			1.0f, // DK?
			1.0f, // Shaman
			1.0f, // Mage
			1.0f, // Warlock
			0.0f, // ??
			1.7f // Druid
			};

	uint8 level = getLevel();
	uint32 pclass = getClass();

	if (level > GT_MAX_LEVEL)
		level = GT_MAX_LEVEL;

	// Dodge per agility for most classes equal crit per agility (but for some classes need apply some multiplier)
	GtChanceToMeleeCritEntry const *dodgeRatio =
			sGtChanceToMeleeCritStore.LookupEntry(
					(pclass - 1) * GT_MAX_LEVEL + level - 1);
	if (dodgeRatio == NULL || pclass > MAX_CLASSES)
		return 0.0f;

	float dodge = dodge_base[pclass - 1]
			+ GetStat(STAT_AGILITY) * dodgeRatio->ratio
					* crit_to_dodge[pclass - 1];
	return dodge * 100.0f;
}

float Player::GetSpellCritFromIntellect() {
	uint8 level = getLevel();
	uint32 pclass = getClass();

	if (level > GT_MAX_LEVEL)
		level = GT_MAX_LEVEL;

	GtChanceToSpellCritBaseEntry const *critBase =
			sGtChanceToSpellCritBaseStore.LookupEntry(pclass - 1);
	GtChanceToSpellCritEntry const *critRatio =
			sGtChanceToSpellCritStore.LookupEntry(
					(pclass - 1) * GT_MAX_LEVEL + level - 1);
	if (critBase == NULL || critRatio == NULL)
		return 0.0f;

	float crit = critBase->base + GetStat(STAT_INTELLECT) * critRatio->ratio;
	return crit * 100.0f;
}

float Player::GetRatingCoefficient(CombatRating cr) const {
	uint8 level = getLevel();

	if (level > GT_MAX_LEVEL)
		level = GT_MAX_LEVEL;

	GtCombatRatingsEntry const *Rating = sGtCombatRatingsStore.LookupEntry(
			cr * GT_MAX_LEVEL + level - 1);
	if (Rating == NULL)
		return 1.0f; // By default use minimum coefficient (not must be called)

	return Rating->ratio;
}

float Player::GetRatingBonusValue(CombatRating cr) const {
	return float(GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr))
			/ GetRatingCoefficient(cr);
}

float Player::GetExpertiseDodgeOrParryReduction(
		WeaponAttackType attType) const {
	switch (attType) {
	case BASE_ATTACK:
		return GetUInt32Value(PLAYER_EXPERTISE) / 4.0f;
	case OFF_ATTACK:
		return GetUInt32Value(PLAYER_OFFHAND_EXPERTISE) / 4.0f;
	default:
		break;
	}
	return 0.0f;
}

float Player::OCTRegenMPPerSpirit() {
	uint8 level = getLevel();
	uint32 pclass = getClass();

	if (level > GT_MAX_LEVEL)
		level = GT_MAX_LEVEL;

	GtRegenMPPerSptEntry const *moreRatio = sGtRegenMPPerSptStore.LookupEntry(
			(pclass - 1) * GT_MAX_LEVEL + level - 1);
	if (moreRatio == NULL)
		return 0.0f;

	// Formula get from PaperDollFrame script
	float spirit = GetStat(STAT_SPIRIT);
	float regen = spirit * moreRatio->ratio;
	return regen;
}

void Player::ApplyRatingMod(CombatRating cr, int32 value, bool apply) {
	m_baseRatingValue[cr] += (apply ? value : -value);

	// explicit affected values
	switch (cr) {
	case CR_HASTE_MELEE: {
		float RatingChange = value / GetRatingCoefficient(cr);
		ApplyAttackTimePercentMod(BASE_ATTACK, RatingChange, apply);
		ApplyAttackTimePercentMod(OFF_ATTACK, RatingChange, apply);
		break;
	}
	case CR_HASTE_RANGED: {
		float RatingChange = value / GetRatingCoefficient(cr);
		ApplyAttackTimePercentMod(RANGED_ATTACK, RatingChange, apply);
		break;
	}
	case CR_HASTE_SPELL: {
		float RatingChange = value / GetRatingCoefficient(cr);
		ApplyCastTimePercentMod(RatingChange, apply);
		break;
	}
	default:
		break;
	}

	UpdateRating(cr);
}

void Player::UpdateRating(CombatRating cr) {
	int32 amount = m_baseRatingValue[cr];
	// Apply bonus from SPELL_AURA_MOD_RATING_FROM_STAT
	// stat used stored in miscValueB for this aura
	AuraEffectList const& modRatingFromStat = GetAuraEffectsByType(
			SPELL_AURA_MOD_RATING_FROM_STAT);
	for (AuraEffectList::const_iterator i = modRatingFromStat.begin();
			i != modRatingFromStat.end(); ++i)
		if ((*i)->GetMiscValue() & (1 << cr))
			amount += int32(
					GetStat(Stats((*i)->GetMiscValueB())) * (*i)->GetAmount()
							/ 100.0f);
	if (amount < 0)
		amount = 0;
	SetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr, uint32(amount));

	bool affectStats = CanModifyStats();

	switch (cr) {
	case CR_WEAPON_SKILL: // Implemented in Unit::RollMeleeOutcomeAgainst
	case CR_DEFENSE_SKILL:
		UpdateDefenseBonusesMod();
		break;
	case CR_DODGE:
		UpdateDodgePercentage();
		break;
	case CR_PARRY:
		UpdateParryPercentage();
		break;
	case CR_BLOCK:
		UpdateBlockPercentage();
		break;
	case CR_HIT_MELEE:
		UpdateMeleeHitChances();
		break;
	case CR_HIT_RANGED:
		UpdateRangedHitChances();
		break;
	case CR_HIT_SPELL:
		UpdateSpellHitChances();
		break;
	case CR_CRIT_MELEE:
		if (affectStats) {
			UpdateCritPercentage(BASE_ATTACK);
			UpdateCritPercentage(OFF_ATTACK);
		}
		break;
	case CR_CRIT_RANGED:
		if (affectStats)
			UpdateCritPercentage(RANGED_ATTACK);
		break;
	case CR_CRIT_SPELL:
		if (affectStats)
			UpdateAllSpellCritChances();
		break;
	case CR_HIT_TAKEN_MELEE: // Implemented in Unit::MeleeMissChanceCalc
	case CR_HIT_TAKEN_RANGED:
		break;
	case CR_HIT_TAKEN_SPELL: // Implemented in Unit::MagicSpellHitResult
		break;
		//case CR_CRIT_TAKEN_MELEE:                           // Implemented in Unit::RollMeleeOutcomeAgainst (only for chance to crit)
		//case CR_CRIT_TAKEN_RANGED:
		//    break;
	case CR_CRIT_TAKEN_SPELL: // Implemented in Unit::SpellCriticalBonus (only for chance to crit)
		break;
	case CR_HASTE_MELEE: // Implemented in Player::ApplyRatingMod
	case CR_HASTE_RANGED:
	case CR_HASTE_SPELL:
		break;
	case CR_WEAPON_SKILL_MAINHAND: // Implemented in Unit::RollMeleeOutcomeAgainst
	case CR_WEAPON_SKILL_OFFHAND:
	case CR_WEAPON_SKILL_RANGED:
		break;
	case CR_MASTERY: // Implemented in Player::UpdateMastery
		UpdateMastery();
		break;
	case CR_EXPERTISE:
		if (affectStats) {
			UpdateExpertise(BASE_ATTACK);
			UpdateExpertise(OFF_ATTACK);
		}
		break;
	case CR_ARMOR_PENETRATION:
		if (affectStats)
			UpdateArmorPenetration(amount);
		break;
	default:
		break;
	}
}

void Player::UpdateAllRatings() {
	for (int cr = 0; cr < MAX_COMBAT_RATING; ++cr)
		UpdateRating(CombatRating(cr));
}

void Player::SetRegularAttackTime() {
	for (uint8 i = 0; i < MAX_ATTACK; ++i) {
		uint32 attackTime = BASE_ATTACK_TIME;
		Item *tmpitem = GetWeaponForAttack(WeaponAttackType(i), true);
		if (tmpitem && !tmpitem->IsBroken()) {
			ItemPrototype const *proto = tmpitem->GetProto();
			if (proto->Delay)
				attackTime = proto->Delay;
		}
		SetAttackTime(WeaponAttackType(i), attackTime); // If there is no weapon reset attack time to base (might have been changed from forms)
	}
}

//skill+step, checking for max value
bool Player::UpdateSkill(uint32 skill_id, uint32 step) {
	if (!skill_id)
		return false;

	if (skill_id == SKILL_FIST_WEAPONS)
		skill_id = SKILL_UNARMED;

	SkillStatusMap::iterator itr = mSkillStatus.find(skill_id);
	if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
		return false;

	uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos);
	uint32 data = GetUInt32Value(valueIndex);
	uint32 value = SKILL_VALUE(data);
	uint32 max = SKILL_MAX(data);

	if ((!max) || (!value) || (value >= max))
		return false;

	if (value < max) {
		uint32 new_value = value + step;
		if (new_value > max)
			new_value = max;

		SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(new_value, max));
		if (itr->second.uState != SKILL_NEW)
			itr->second.uState = SKILL_CHANGED;
		UpdateSkillEnchantments(skill_id, value, new_value);
		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, skill_id);
		return true;
	}

	return false;
}

inline int SkillGainChance(uint32 SkillValue, uint32 GrayLevel,
		uint32 GreenLevel, uint32 YellowLevel) {
	if (SkillValue >= GrayLevel)
		return sWorld->getIntConfig(CONFIG_SKILL_CHANCE_GREY) * 10;
	if (SkillValue >= GreenLevel)
		return sWorld->getIntConfig(CONFIG_SKILL_CHANCE_GREEN) * 10;
	if (SkillValue >= YellowLevel)
		return sWorld->getIntConfig(CONFIG_SKILL_CHANCE_YELLOW) * 10;
	return sWorld->getIntConfig(CONFIG_SKILL_CHANCE_ORANGE) * 10;
}

bool Player::UpdateCraftSkill(uint32 spellid) {
	sLog->outDebug(LOG_FILTER_PLAYER_SKILLS, "UpdateCraftSkill spellid %d",
			spellid);

	SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(
			spellid);

	for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first;
			_spell_idx != bounds.second; ++_spell_idx) {
		if (_spell_idx->second->skillId) {
			uint32 SkillValue = GetPureSkillValue(_spell_idx->second->skillId);

			// Alchemy Discoveries here
			SpellEntry const* spellEntry = sSpellStore.LookupEntry(spellid);
			if (spellEntry && spellEntry->Mechanic == MECHANIC_DISCOVERY) {
				if (uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->skillId, spellid, this))
					learnSpell(discoveredSpell, false);
			}

			uint32 craft_skill_gain = sWorld->getIntConfig(
					CONFIG_SKILL_GAIN_CRAFTING);

			return UpdateSkillPro(
					_spell_idx->second->skillId,
					SkillGainChance(
							SkillValue,
							_spell_idx->second->max_value,
							(_spell_idx->second->max_value
									+ _spell_idx->second->min_value) / 2,
							_spell_idx->second->min_value), craft_skill_gain);
		}
	}
	return false;
}

bool Player::UpdateGatherSkill(uint32 SkillId, uint32 SkillValue,
		uint32 RedLevel, uint32 Multiplicator) {
	sLog->outDebug(LOG_FILTER_PLAYER_SKILLS,
			"UpdateGatherSkill(SkillId %d SkillLevel %d RedLevel %d)", SkillId,
			SkillValue, RedLevel);

	uint32 gathering_skill_gain = sWorld->getIntConfig(
			CONFIG_SKILL_GAIN_GATHERING);

	// For skinning and Mining chance decrease with level. 1-74 - no decrease, 75-149 - 2 times, 225-299 - 8 times
	switch (SkillId) {
	case SKILL_HERBALISM:
	case SKILL_LOCKPICKING:
	case SKILL_JEWELCRAFTING:
	case SKILL_INSCRIPTION:
		return UpdateSkillPro(
				SkillId,
				SkillGainChance(SkillValue, RedLevel + 100, RedLevel + 50,
						RedLevel + 25) * Multiplicator, gathering_skill_gain);
	case SKILL_SKINNING:
		if (sWorld->getIntConfig(CONFIG_SKILL_CHANCE_SKINNING_STEPS) == 0)
			return UpdateSkillPro(
					SkillId,
					SkillGainChance(SkillValue, RedLevel + 100, RedLevel + 50,
							RedLevel + 25) * Multiplicator,
					gathering_skill_gain);
		else
			return UpdateSkillPro(
					SkillId,
					(SkillGainChance(SkillValue, RedLevel + 100, RedLevel + 50,
							RedLevel + 25) * Multiplicator)
							>> (SkillValue
									/ sWorld->getIntConfig(
											CONFIG_SKILL_CHANCE_SKINNING_STEPS)),
					gathering_skill_gain);
	case SKILL_MINING:
		if (sWorld->getIntConfig(CONFIG_SKILL_CHANCE_MINING_STEPS) == 0)
			return UpdateSkillPro(
					SkillId,
					SkillGainChance(SkillValue, RedLevel + 100, RedLevel + 50,
							RedLevel + 25) * Multiplicator,
					gathering_skill_gain);
		else
			return UpdateSkillPro(
					SkillId,
					(SkillGainChance(SkillValue, RedLevel + 100, RedLevel + 50,
							RedLevel + 25) * Multiplicator)
							>> (SkillValue
									/ sWorld->getIntConfig(
											CONFIG_SKILL_CHANCE_MINING_STEPS)),
					gathering_skill_gain);
	}
	return false;
}

bool Player::UpdateFishingSkill() {
	sLog->outDebug(LOG_FILTER_PLAYER_SKILLS, "UpdateFishingSkill");

	uint32 SkillValue = GetPureSkillValue(SKILL_FISHING);

	float chance = 0.0f;

	if (SkillValue <= 115)
		chance = 100.0f;
	else if (SkillValue <= 150)
		chance = 50.0f;
	else if (SkillValue <= 170)
		chance = 33.33f;
	else if (SkillValue <= 190)
		chance = 25.0f;
	else if (SkillValue <= 215)
		chance = 20.0f;
	else if (SkillValue <= 235)
		chance = 16.67f;
	else if (SkillValue <= 260)
		chance = 14.29f;
	else if (SkillValue <= 280)
		chance = 12.5f;
	else if (SkillValue <= 325)
		chance = 11.11f;
	else if (SkillValue <= 365)
		chance = 10.0f;
	else if (SkillValue <= 450)
		chance = 9.1f;
	else if (SkillValue <= 500)
		chance = 11.11f;
	else
		chance = 10.0f;

	uint32 gathering_skill_gain = sWorld->getIntConfig(
			CONFIG_SKILL_GAIN_GATHERING);

	return UpdateSkillPro(SKILL_FISHING, (int32) (chance * 10.0f),
			gathering_skill_gain);
}

// levels sync. with spell requirement for skill levels to learn
// bonus abilities in sSkillLineAbilityStore
// Used only to avoid scan DBC at each skill grow
static uint32 bonusSkillLevels[] = { 75, 150, 225, 300, 375, 450, 525 };

bool Player::UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step) {
	sLog->outDebug(LOG_FILTER_PLAYER_SKILLS,
			"UpdateSkillPro(SkillId %d, Chance %3.1f%%)", SkillId,
			Chance / 10.0);
	if (!SkillId)
		return false;

	if (Chance <= 0) // speedup in 0 chance case
			{
		sLog->outDebug(LOG_FILTER_PLAYER_SKILLS,
				"Player::UpdateSkillPro Chance=%3.1f%% missed", Chance / 10.0);
		return false;
	}

	SkillStatusMap::iterator itr = mSkillStatus.find(SkillId);
	if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
		return false;

	uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos);

	uint32 data = GetUInt32Value(valueIndex);
	uint16 SkillValue = SKILL_VALUE(data);
	uint16 MaxValue = SKILL_MAX(data);

	if (!MaxValue || !SkillValue || SkillValue >= MaxValue)
		return false;

	int32 Roll = irand(1, 1000);

	if (Roll <= Chance) {
		uint32 new_value = SkillValue + step;
		if (new_value > MaxValue)
			new_value = MaxValue;

		SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(new_value, MaxValue));
		if (itr->second.uState != SKILL_NEW)
			itr->second.uState = SKILL_CHANGED;
		for (uint32* bsl = &bonusSkillLevels[0]; *bsl; ++bsl) {
			if ((SkillValue < *bsl && new_value >= *bsl)) {
				learnSkillRewardedSpells(SkillId, new_value);
				break;
			}
		}
		UpdateSkillEnchantments(SkillId, SkillValue, new_value);
		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, SkillId);
		sLog->outDebug(LOG_FILTER_PLAYER_SKILLS,
				"Player::UpdateSkillPro Chance=%3.1f%% taken", Chance / 10.0);
		return true;
	}

	sLog->outDebug(LOG_FILTER_PLAYER_SKILLS,
			"Player::UpdateSkillPro Chance=%3.1f%% missed", Chance / 10.0);
	return false;
}

void Player::UpdateWeaponSkill(WeaponAttackType attType) 
{
    // no skill gain in pvp
    Unit* victim = getVictim();
    if (victim && victim->GetTypeId() == TYPEID_PLAYER)
        return;

    if (IsInFeralForm())
        return;                                             // always maximized SKILL_FERAL_COMBAT in fact

    if (GetShapeshiftForm() == FORM_TREE)
        return;                                             // use weapon but not skill up

    if (victim && victim->GetTypeId() == TYPEID_UNIT && (victim->ToCreature()->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_SKILLGAIN))
        return;

    uint32 weapon_skill_gain = sWorld->getIntConfig(CONFIG_SKILL_GAIN_WEAPON);

    Item* tmpitem = GetWeaponForAttack(attType, true);
    if (!tmpitem && attType == BASE_ATTACK)
        UpdateSkill(SKILL_UNARMED, weapon_skill_gain);
    else if (tmpitem && tmpitem->GetProto()->SubClass != ITEM_SUBCLASS_WEAPON_FISHING_POLE)
        UpdateSkill(tmpitem->GetSkill(), weapon_skill_gain);

    UpdateAllCritPercentages();
}

void Player::UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType,
		bool defence) {
	uint8 plevel = getLevel(); // if defense than pVictim == attacker
	uint8 greylevel = Trinity::XP::GetGrayLevel(plevel);
	uint8 moblevel = pVictim->getLevelForTarget(this);
	if (moblevel < greylevel)
		return;

	if (moblevel > plevel + 5)
		moblevel = plevel + 5;

	uint8 lvldif = moblevel - greylevel;
	if (lvldif < 3)
		lvldif = 3;

	uint32 skilldif = 5 * plevel
			- (defence ?
					GetBaseDefenseSkillValue() :
					GetBaseWeaponSkillValue(attType));
	if (skilldif <= 0)
		return;

	float chance = float(3 * lvldif * skilldif) / plevel;
	if (!defence)
		if (getClass() == CLASS_WARRIOR || getClass() == CLASS_ROGUE)
			chance += chance * 0.02f * GetStat(STAT_INTELLECT);

	chance = chance < 1.0f ? 1.0f : chance; //minimum chance to increase skill is 1%

	if (roll_chance_f(chance)) {
		if (defence)
			UpdateDefense();
		else
			UpdateWeaponSkill(attType);
	} else
		return;
}

void Player::ModifySkillBonus(uint32 skillid, int32 val, bool talent) {
	SkillStatusMap::const_iterator itr = mSkillStatus.find(skillid);
	if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
		return;

	uint32 bonusIndex = PLAYER_SKILL_BONUS_INDEX(itr->second.pos);

	uint32 bonus_val = GetUInt32Value(bonusIndex);
	int16 temp_bonus = SKILL_TEMP_BONUS(bonus_val);
	int16 perm_bonus = SKILL_PERM_BONUS(bonus_val);

	if (talent) // permanent bonus stored in high part
		SetUInt32Value(bonusIndex,
				MAKE_SKILL_BONUS(temp_bonus, perm_bonus+val));
	else
		// temporary/item bonus stored in low part
		SetUInt32Value(bonusIndex,
				MAKE_SKILL_BONUS(temp_bonus+val, perm_bonus));
}

void Player::UpdateSkillsForLevel() {
	uint16 maxconfskill = sWorld->GetConfigMaxSkillValue();
	uint32 maxSkill = GetMaxSkillValueForLevel();

	for (SkillStatusMap::iterator itr = mSkillStatus.begin();
			itr != mSkillStatus.end(); ++itr) {
		if (itr->second.uState == SKILL_DELETED)
			continue;

		uint32 pskill = itr->first;
		SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(pskill);
		if (!pSkill)
			continue;

		if (GetSkillRangeType(pSkill, false) != SKILL_RANGE_LEVEL)
			continue;

		uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos);
		uint32 data = GetUInt32Value(valueIndex);
		uint32 max = SKILL_MAX(data);
		uint32 val = SKILL_VALUE(data);

		/// update only level dependent max skill values
		if (max != 1) {
			/// maximize skill always
			if (!sWorld->getBoolConfig(CONFIG_USE_OLD_SKILL_SYSTEM)) {
				SetUInt32Value(valueIndex,
						MAKE_SKILL_VALUE(maxSkill, maxSkill));
				if (itr->second.uState != SKILL_NEW)
					itr->second.uState = SKILL_CHANGED;
			} else if (max != maxconfskill) /// update max skill value if current max skill not maximized
					{
				SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(val, maxSkill));
				if (itr->second.uState != SKILL_NEW)
					itr->second.uState = SKILL_CHANGED;
			}
		}
	}
}

void Player::UpdateSkillsToMaxSkillsForLevel() {
	for (SkillStatusMap::iterator itr = mSkillStatus.begin();
			itr != mSkillStatus.end(); ++itr) {
		if (itr->second.uState == SKILL_DELETED)
			continue;

		uint32 pskill = itr->first;
		if (IsProfessionOrRidingSkill(pskill))
			continue;
		uint32 valueIndex = PLAYER_SKILL_VALUE_INDEX(itr->second.pos);
		uint32 data = GetUInt32Value(valueIndex);
		uint32 max = SKILL_MAX(data);

		if (max > 1) {
			SetUInt32Value(valueIndex, MAKE_SKILL_VALUE(max, max));
			if (itr->second.uState != SKILL_NEW)
				itr->second.uState = SKILL_CHANGED;
		}
		if (pskill == SKILL_DEFENSE)
			UpdateDefenseBonusesMod();
	}
}

// This functions sets a skill line value (and adds if doesn't exist yet)
// To "remove" a skill line, set it's values to zero
void Player::SetSkill(uint16 id, uint16 step, uint16 newVal, uint16 maxVal) {
	if (!id)
		return;

	uint16 currVal;
	SkillStatusMap::iterator itr = mSkillStatus.find(id);

	//has skill
	if (itr != mSkillStatus.end() && itr->second.uState != SKILL_DELETED) {
		currVal =
				SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos)));
		if (newVal) {
			// if skill value is going down, update enchantments before setting the new value
			if (newVal < currVal)
				UpdateSkillEnchantments(id, currVal, newVal);
			// update step
			SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos),
					MAKE_PAIR32(id, step));
			// update value
			SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos),
					MAKE_SKILL_VALUE(newVal, maxVal));
			if (itr->second.uState != SKILL_NEW)
				itr->second.uState = SKILL_CHANGED;
			learnSkillRewardedSpells(id, newVal);
			// if skill value is going up, update enchantments after setting the new value
			if (newVal > currVal)
				UpdateSkillEnchantments(id, currVal, newVal);
			GetAchievementMgr().UpdateAchievementCriteria(
					ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, id);
			GetAchievementMgr().UpdateAchievementCriteria(
					ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL, id);
		} else //remove
		{
			//remove enchantments needing this skill
			UpdateSkillEnchantments(id, currVal, 0);
			// clear skill fields
			SetUInt32Value(PLAYER_SKILL_INDEX(itr->second.pos), 0);
			SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos), 0);
			SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos), 0);

			// mark as deleted or simply remove from map if not saved yet
			if (itr->second.uState != SKILL_NEW)
				itr->second.uState = SKILL_DELETED;
			else
				mSkillStatus.erase(itr);

			// remove all spells that related to this skill
			for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
				if (SkillLineAbilityEntry const *pAbility = sSkillLineAbilityStore.LookupEntry(j))
					if (pAbility->skillId == id)
						removeSpell(
								sSpellMgr->GetFirstSpellInChain(
										pAbility->spellId));
		}
	} else if (newVal) //add
	{
		currVal = 0;
		for (int i = 0; i < PLAYER_MAX_SKILLS; ++i)
			if (!GetUInt32Value(PLAYER_SKILL_INDEX(i))) {
				SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(id);
				if (!pSkill) {
					sLog->outError(
							"Skill not found in SkillLineStore: skill #%u", id);
					return;
				}

				SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id, step));
				SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),
						MAKE_SKILL_VALUE(newVal, maxVal));
				UpdateSkillEnchantments(id, currVal, newVal);
				GetAchievementMgr().UpdateAchievementCriteria(
						ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, id);
				GetAchievementMgr().UpdateAchievementCriteria(
						ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL, id);

				// insert new entry or update if not deleted old entry yet
				if (itr != mSkillStatus.end()) {
					itr->second.pos = i;
					itr->second.uState = SKILL_CHANGED;
				} else
					mSkillStatus.insert(
							SkillStatusMap::value_type(id,
									SkillStatusData(i, SKILL_NEW)));

				// apply skill bonuses
				SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i), 0);

				// temporary bonuses
				AuraEffectList const& mModSkill = GetAuraEffectsByType(
						SPELL_AURA_MOD_SKILL);
				for (AuraEffectList::const_iterator j = mModSkill.begin();
						j != mModSkill.end(); ++j)
					if ((*j)->GetMiscValue() == int32(id))
						(*j)->HandleEffect(this, 0, true);

				// permanent bonuses
				AuraEffectList const& mModSkillTalent = GetAuraEffectsByType(
						SPELL_AURA_MOD_SKILL_TALENT);
				for (AuraEffectList::const_iterator j = mModSkillTalent.begin();
						j != mModSkillTalent.end(); ++j)
					if ((*j)->GetMiscValue() == int32(id))
						(*j)->HandleEffect(this, 0, true);

				// Learn all spells for skill
				learnSkillRewardedSpells(id, newVal);
				return;
			}
	}
}

bool Player::HasSkill(uint32 skill) const {
	if (!skill)
		return false;

	SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
	return (itr != mSkillStatus.end() && itr->second.uState != SKILL_DELETED);
}

uint16 Player::GetSkillStep(uint16 skill) const {
	if (!skill)
		return 0;

	SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
	if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
		return 0;

	return PAIR32_HIPART(GetUInt32Value(PLAYER_SKILL_INDEX(itr->second.pos)));
}

uint16 Player::GetSkillValue(uint32 skill) const {
	if (!skill)
		return 0;

	SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
	if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
		return 0;

	uint32 bonus = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos));

	int32 result =
			int32(
					SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos))));
	result += SKILL_TEMP_BONUS(bonus);
	result += SKILL_PERM_BONUS(bonus);
	return result < 0 ? 0 : result;
}

uint16 Player::GetMaxSkillValue(uint32 skill) const {
	if (!skill)
		return 0;

	SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
	if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
		return 0;

	uint32 bonus = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos));

	int32 result =
			int32(
					SKILL_MAX(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos))));
	result += SKILL_TEMP_BONUS(bonus);
	result += SKILL_PERM_BONUS(bonus);
	return result < 0 ? 0 : result;
}

uint16 Player::GetPureMaxSkillValue(uint32 skill) const {
	if (!skill)
		return 0;

	SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
	if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
		return 0;

	return SKILL_MAX(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos)));
}

uint16 Player::GetBaseSkillValue(uint32 skill) const {
	if (!skill)
		return 0;

	SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
	if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
		return 0;

	int32 result =
			int32(
					SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos))));
	result +=
			SKILL_PERM_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos)));
	return result < 0 ? 0 : result;
}

uint16 Player::GetPureSkillValue(uint32 skill) const {
	if (!skill)
		return 0;

	SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
	if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
		return 0;

	return SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(itr->second.pos)));
}

int16 Player::GetSkillPermBonusValue(uint32 skill) const {
	if (!skill)
		return 0;

	SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
	if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
		return 0;

	return SKILL_PERM_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos)));
}

int16 Player::GetSkillTempBonusValue(uint32 skill) const {
	if (!skill)
		return 0;

	SkillStatusMap::const_iterator itr = mSkillStatus.find(skill);
	if (itr == mSkillStatus.end() || itr->second.uState == SKILL_DELETED)
		return 0;

	return SKILL_TEMP_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(itr->second.pos)));
}

void Player::SendActionButtons(uint32 state) const {
	sLog->outDetail("Sending Action Buttons for '%u' spec '%u'", GetGUIDLow(),
			m_activeSpec);

	WorldPacket data(SMSG_ACTION_BUTTONS, 1 + (MAX_ACTION_BUTTONS * 4));
	data << uint8(state);
	/*
	 state can be 0, 1, 2
	 0 - Looks to be sent when initial action buttons get sent, however on Trinity we use 1 since 0 had some difficulties
	 1 - Used in any SMSG_ACTION_BUTTONS packet with button data on Trinity. Only used after spec swaps on retail.
	 2 - Clears the action bars client sided. This is sent during spec swap before unlearning and before sending the new buttons
	 */
	if (state != 2) {
		for (uint8 button = 0; button < MAX_ACTION_BUTTONS; ++button) {
			ActionButtonList::const_iterator itr = m_actionButtons.find(button);
			if (itr != m_actionButtons.end()
					&& itr->second.uState != ACTIONBUTTON_DELETED)
				data << uint32(itr->second.packedData);
			else
				data << uint32(0);
		}
	}

	GetSession()->SendPacket(&data);
	sLog->outDetail("Action Buttons for '%u' spec '%u' Sent", GetGUIDLow(),
			m_activeSpec);
}

bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type) {
	if (button >= MAX_ACTION_BUTTONS) {
		sLog->outError(
				"Action %u not added into button %u for player %s: button must be < %u",
				action, button, GetName(), MAX_ACTION_BUTTONS);
		return false;
	}

	if (action >= MAX_ACTION_BUTTON_ACTION_VALUE) {
		sLog->outError(
				"Action %u not added into button %u for player %s: action must be < %u",
				action, button, GetName(), MAX_ACTION_BUTTON_ACTION_VALUE);
		return false;
	}

	switch (type) {
	case ACTION_BUTTON_SPELL:
		if (!sSpellStore.LookupEntry(action)) {
			sLog->outError(
					"Spell action %u not added into button %u for player %s: spell not exist",
					action, button, GetName());
			return false;
		}

		if (!HasSpell(action)) {
			sLog->outError(
					"Spell action %u not added into button %u for player %s: player don't known this spell",
					action, button, GetName());
			return false;
		}
		break;
	case ACTION_BUTTON_ITEM:
		if (!ObjectMgr::GetItemPrototype(action)) {
			sLog->outError(
					"Item action %u not added into button %u for player %s: item not exist",
					action, button, GetName());
			return false;
		}
		break;
	default:
		break; // other cases not checked at this moment
	}

	return true;
}

ActionButton* Player::addActionButton(uint8 button, uint32 action, uint8 type) {
	if (!IsActionButtonDataValid(button, action, type))
		return NULL;

	// it create new button (NEW state) if need or return existed
	ActionButton& ab = m_actionButtons[button];

	// set data and update to CHANGED if not NEW
	ab.SetActionAndType(action, ActionButtonType(type));

	sLog->outDetail("Player '%u' Added Action '%u' (type %u) to Button '%u'",
			GetGUIDLow(), action, type, button);
	return &ab;
}

void Player::removeActionButton(uint8 button) {
	ActionButtonList::iterator buttonItr = m_actionButtons.find(button);
	if (buttonItr == m_actionButtons.end()
			|| buttonItr->second.uState == ACTIONBUTTON_DELETED)
		return;

	if (buttonItr->second.uState == ACTIONBUTTON_NEW)
		m_actionButtons.erase(buttonItr); // new and not saved
	else
		buttonItr->second.uState = ACTIONBUTTON_DELETED; // saved, will deleted at next save

	sLog->outDetail("Action Button '%u' Removed from Player '%u'", button,
			GetGUIDLow());
}

ActionButton const* Player::GetActionButton(uint8 button) {
	ActionButtonList::iterator buttonItr = m_actionButtons.find(button);
	if (buttonItr == m_actionButtons.end()
			|| buttonItr->second.uState == ACTIONBUTTON_DELETED)
		return NULL;

	return &buttonItr->second;
}

bool Player::SetPosition(float x, float y, float z, float orientation,
		bool teleport) {
	if (!Unit::SetPosition(x, y, z, orientation, teleport))
		return false;

	//if (movementInfo.flags & MOVEMENTFLAG_MOVING)
	//    mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOVE);
	//if (movementInfo.flags & MOVEMENTFLAG_TURNING)
	//    mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING);
	//AURA_INTERRUPT_FLAG_JUMP not sure

	// group update
	if (GetGroup())
		SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION);

	// code block for underwater state update
	// Unit::SetPosition() checks for validity and updates our coordinates
	// so we re-fetch them instead of using "raw" coordinates from function params
	UpdateUnderwaterState(GetMap(), GetPositionX(), GetPositionY(),
			GetPositionZ());

	if (GetTrader() && !IsWithinDistInMap(GetTrader(), INTERACTION_DISTANCE))
		GetSession()->SendCancelTrade();

	CheckAreaExploreAndOutdoor();

	return true;
}

void Player::SaveRecallPosition() {
	m_recallMap = GetMapId();
	m_recallX = GetPositionX();
	m_recallY = GetPositionY();
	m_recallZ = GetPositionZ();
	m_recallO = GetOrientation();
}

void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self) {
	if (self)
		GetSession()->SendPacket(data);

	Trinity::MessageDistDeliverer notifier(this, data, dist);
	VisitNearbyWorldObject(dist, notifier);
}

void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self,
		bool own_team_only) {
	if (self)
		GetSession()->SendPacket(data);

	Trinity::MessageDistDeliverer notifier(this, data, dist, own_team_only);
	VisitNearbyWorldObject(dist, notifier);
}

void Player::SendMessageToSet(WorldPacket *data, Player const* skipped_rcvr) {
	if (skipped_rcvr != this)
		GetSession()->SendPacket(data);

	// we use World::GetMaxVisibleDistance() because i cannot see why not use a distance
	// update: replaced by GetMap()->GetVisibilityDistance()
	Trinity::MessageDistDeliverer notifier(this, data, GetVisibilityRange(),
			false, skipped_rcvr);
	VisitNearbyWorldObject(GetVisibilityRange(), notifier);
}

void Player::SendDirectMessage(WorldPacket *data) {
	if (m_session)
		m_session->SendPacket(data);
}

void Player::SendCinematicStart(uint32 CinematicSequenceId) {
	WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4);
	data << uint32(CinematicSequenceId);
	SendDirectMessage(&data);
}

void Player::SendMovieStart(uint32 MovieId) {
	WorldPacket data(SMSG_TRIGGER_MOVIE, 4);
	data << uint32(MovieId);
	SendDirectMessage(&data);
}

bool Player::isInWorgenForm() {
	return GetUInt32Value(UNIT_FIELD_FLAGS_2) & IN_WORGEN_FORM ? true : false;
}

void Player::setInHumanForm() {
	if (isInCombat())
		return;

	uint32 newFlag = GetUInt32Value(UNIT_FIELD_FLAGS_2) & ~IN_WORGEN_FORM;
	SetUInt32Value(UNIT_FIELD_FLAGS_2, newFlag);
	m_ExtraFlags &= ~PLAYER_EXTRA_WORGEN_FORM;
}

void Player::setInWorgenForm(uint32 form) {
	if (isInWorgenForm())
		return;
	SetFlag(UNIT_FIELD_FLAGS_2, form);
	m_ExtraFlags |= PLAYER_EXTRA_WORGEN_FORM;
}

bool Player::toggleWorgenForm(uint32 form) {
	if (isInWorgenForm()) {
		setInHumanForm();
		return false;
	} else {
		setInWorgenForm(form);
		return true;
	}
}

void Player::CheckAreaExploreAndOutdoor() {
	if (!isAlive())
		return;

	if (isInFlight())
		return;

	if (!m_AreaID)
		m_AreaID = GetAreaId();
	if (m_AreaID != GetAreaId())
		m_AreaID = GetAreaId();

	bool isOutdoor;
	uint16 areaFlag = GetBaseMap()->GetAreaFlag(GetPositionX(), GetPositionY(),
			GetPositionZ(), &isOutdoor);

	if (sWorld->getBoolConfig(CONFIG_VMAP_INDOOR_CHECK) && !isOutdoor)
		RemoveAurasWithAttribute(SPELL_ATTR0_OUTDOORS_ONLY);

	if (areaFlag == 0xffff)
		return;
	int offset = areaFlag / 32;

	if (offset >= PLAYER_EXPLORED_ZONES_SIZE) {
		sLog->outError(
				"Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < %u ).",
				areaFlag, GetPositionX(), GetPositionY(), offset, offset,
				PLAYER_EXPLORED_ZONES_SIZE);
		return;
	}

	uint32 val = (uint32) (1 << (areaFlag % 32));
	uint32 currFields = GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset);

	if (!(currFields & val)) {
		SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset,
				(uint32) (currFields | val));

		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA);

		AreaTableEntry const *p = GetAreaEntryByAreaFlagAndMap(areaFlag,
				GetMapId());
		if (!p) {
			sLog->outError(
					"PLAYER: Player %u discovered unknown area (x: %f y: %f map: %u",
					GetGUIDLow(), GetPositionX(), GetPositionY(), GetMapId());
		} else if (p->area_level > 0) {
			uint32 area = p->ID;
			if (getLevel() >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) {
				SendExplorationExperience(area, 0);
			} else {
				int32 diff = int32(getLevel()) - p->area_level;
				uint32 XP = 0;
				if (diff < -5) {
					XP = uint32(
							sObjectMgr->GetBaseXP(getLevel() + 5)
									* sWorld->getRate(RATE_XP_EXPLORE));
				} else if (diff > 5) {
					int32 exploration_percent = (100 - ((diff - 5) * 5));
					if (exploration_percent > 100)
						exploration_percent = 100;
					else if (exploration_percent < 0)
						exploration_percent = 0;

					XP = uint32(
							sObjectMgr->GetBaseXP(p->area_level)
									* exploration_percent / 100
									* sWorld->getRate(RATE_XP_EXPLORE));
				} else {
					XP = uint32(
							sObjectMgr->GetBaseXP(p->area_level)
									* sWorld->getRate(RATE_XP_EXPLORE));
				}

				GiveXP(XP, NULL);
				SendExplorationExperience(area, XP);
			}
			sLog->outDetail("PLAYER: Player %u discovered a new area: %u",
					GetGUIDLow(), area);
		}
	}
}

uint32 Player::TeamForRace(uint8 race) {
	if (ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race)) {
		switch (rEntry->TeamID) {
		case 1:
			return HORDE;
		case 7:
			return ALLIANCE;
		}
		sLog->outError(
				"Race (%u) has wrong teamid (%u) in DBC: wrong DBC files?",
				uint32(race), rEntry->TeamID);
	} else
		sLog->outError("Race (%u) not found in DBC: wrong DBC files?",
				uint32(race));

	return ALLIANCE;
}

void Player::setFactionForRace(uint8 race) {
	m_team = TeamForRace(race);

	ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race);
	setFaction(rEntry ? rEntry->FactionID : 0);
}

ReputationRank Player::GetReputationRank(uint32 faction) const {
	FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction);
	return GetReputationMgr().GetRank(factionEntry);
}

//Calculate total reputation percent player gain with quest/creature level
int32 Player::CalculateReputationGain(uint32 creatureOrQuestLevel, int32 rep,
		int32 faction, bool for_quest, bool noQuestBonus) {
	float percent = 100.0f;

	// Get the generic rate first
	if (RepRewardRate const * repData = sObjectMgr->GetRepRewardRate(faction)) {
		float repRate =
				for_quest ? repData->quest_rate : repData->creature_rate;
		percent *= repRate;
	}

	float rate =
			for_quest ?
					sWorld->getRate(RATE_REPUTATION_LOWLEVEL_QUEST) :
					sWorld->getRate(RATE_REPUTATION_LOWLEVEL_KILL);

	if (rate != 1.0f
			&& creatureOrQuestLevel <= Trinity::XP::GetGrayLevel(getLevel()))
		percent *= rate;

	float repMod =
			noQuestBonus ?
					0.0f :
					(float) GetTotalAuraModifier(
							SPELL_AURA_MOD_REPUTATION_GAIN);

	if (!for_quest)
		repMod += GetTotalAuraModifierByMiscValue(
				SPELL_AURA_MOD_FACTION_REPUTATION_GAIN, faction);

	percent += rep > 0 ? repMod : -repMod;

	if (percent <= 0.0f)
		return 0;

	return int32(rep * percent / 100);
}

//Calculates how many reputation points player gains in victim's enemy factions
void Player::RewardReputation(Unit *pVictim, float rate) {
	if (!pVictim || pVictim->GetTypeId() == TYPEID_PLAYER)
		return;

	if (pVictim->ToCreature()->IsReputationGainDisabled())
		return;

	ReputationOnKillEntry const* Rep = sObjectMgr->GetReputationOnKilEntry(
			pVictim->ToCreature()->GetCreatureInfo()->Entry);

	if (!Rep)
		return;

	uint32 ChampioningFaction = 0;

	if (GetChampioningFaction()) {
		// support for: Championing - http://www.wowwiki.com/Championing

		Map const *pMap = GetMap();
		if (pMap && pMap->IsDungeon()) {
			InstanceTemplate const *pInstance = sObjectMgr->GetInstanceTemplate(
					pMap->GetId());
			if (pInstance) {
				AccessRequirement const *pAccessRequirement =
						sObjectMgr->GetAccessRequirement(pMap->GetId(),
								((InstanceMap*) pMap)->GetDifficulty());
				if (pAccessRequirement) {
					if (!pMap->IsRaid() && pAccessRequirement->levelMin == 80)
						ChampioningFaction = GetChampioningFaction();
				}
			}
		}
	}

	// Favored reputation increase START
	uint32 zone = GetZoneId();
	uint32 team = GetTeam();
	float favored_rep_mult = 0;

	if ((HasAura(32096) || HasAura(32098))
			&& (zone == 3483 || zone == 3562 || zone == 3836 || zone == 3713
					|| zone == 3714))
		favored_rep_mult = 0.25; // Thrallmar's Favor and Honor Hold's Favor
	else if (HasAura(30754)
			&& (Rep->repfaction1 == 609 || Rep->repfaction2 == 609)
			&& !ChampioningFaction)
		favored_rep_mult = 0.25; // Cenarion Favor

	if (favored_rep_mult > 0)
		favored_rep_mult *= 2; // Multiplied by 2 because the reputation is divided by 2 for some reason (See "donerep1 / 2" and "donerep2 / 2") -- if you know why this is done, please update/explain :)
	// Favored reputation increase END

	bool recruitAFriend = GetsRecruitAFriendBonus(false);

	if (Rep->repfaction1 && (!Rep->team_dependent || team == ALLIANCE)) {
		int32 donerep1 = CalculateReputationGain(pVictim->getLevel(),
				Rep->repvalue1,
				ChampioningFaction ? ChampioningFaction : Rep->repfaction1,
				false);
		donerep1 = int32(donerep1 * (rate + favored_rep_mult));

		if (recruitAFriend)
			donerep1 =
					int32(
							donerep1
									* (1
											+ sWorld->getRate(
													RATE_REPUTATION_RECRUIT_A_FRIEND_BONUS)));

		FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(
				ChampioningFaction ? ChampioningFaction : Rep->repfaction1);
		uint32 current_reputation_rank1 = GetReputationMgr().GetRank(
				factionEntry1);
		if (factionEntry1
				&& current_reputation_rank1 <= Rep->reputation_max_cap1)
			GetReputationMgr().ModifyReputation(factionEntry1, donerep1);
	}

	if (Rep->repfaction2 && (!Rep->team_dependent || team == HORDE)) {
		int32 donerep2 = CalculateReputationGain(pVictim->getLevel(),
				Rep->repvalue2,
				ChampioningFaction ? ChampioningFaction : Rep->repfaction2,
				false);
		donerep2 = int32(donerep2 * (rate + favored_rep_mult));

		if (recruitAFriend)
			donerep2 =
					int32(
							donerep2
									* (1
											+ sWorld->getRate(
													RATE_REPUTATION_RECRUIT_A_FRIEND_BONUS)));

		FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(
				ChampioningFaction ? ChampioningFaction : Rep->repfaction2);
		uint32 current_reputation_rank2 = GetReputationMgr().GetRank(
				factionEntry2);
		if (factionEntry2
				&& current_reputation_rank2 <= Rep->reputation_max_cap2)
			GetReputationMgr().ModifyReputation(factionEntry2, donerep2);
	}
	
    if (Rep->currencyid1 && Rep->currencycount1)
    {
        ModifyCurrency(Rep->currencyid1, Rep->currencycount1);
    }

    if (Rep->currencyid2 && Rep->currencycount2)
    {
        ModifyCurrency(Rep->currencyid2, Rep->currencycount2);
    }

    if (Rep->currencyid3 && Rep->currencycount3)
    {
        ModifyCurrency(Rep->currencyid3, Rep->currencycount3);
    }
	
}




//Calculate how many reputation points player gain with the quest
void Player::RewardReputation(Quest const *pQuest) {
	bool recruitAFriend = GetsRecruitAFriendBonus(false);

	// quest reputation reward/loss
	for (uint8 i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) {
		if (!pQuest->RewRepFaction[i])
			continue;
		if (pQuest->RewRepValue[i]) {
			int32 rep = CalculateReputationGain(GetQuestLevel(pQuest),
					pQuest->RewRepValue[i] / 100, pQuest->RewRepFaction[i],
					true, true);

			if (recruitAFriend)
				rep =
						int32(
								rep
										* (1
												+ sWorld->getRate(
														RATE_REPUTATION_RECRUIT_A_FRIEND_BONUS)));

			if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(pQuest->RewRepFaction[i]))
				GetReputationMgr().ModifyReputation(factionEntry, rep);
		} else {
			uint32 row = ((pQuest->RewRepValueId[i] < 0) ? 1 : 0) + 1;
			uint32 field = abs(pQuest->RewRepValueId[i]);

			if (const QuestFactionRewEntry *pRow = sQuestFactionRewardStore.LookupEntry(row)) {
				int32 repPoints = pRow->QuestRewFactionValue[field];

				if (!repPoints)
					continue;

				repPoints = CalculateReputationGain(GetQuestLevel(pQuest),
						repPoints, pQuest->RewRepFaction[i], true);

				if (recruitAFriend)
					repPoints =
							int32(
									repPoints
											* (1
													+ sWorld->getRate(
															RATE_REPUTATION_RECRUIT_A_FRIEND_BONUS)));

				if (const FactionEntry* factionEntry = sFactionStore.LookupEntry(pQuest->RewRepFaction[i]))
					GetReputationMgr().ModifyReputation(factionEntry,
							repPoints);
			}
		}
	}
}

void Player::UpdateHonorFields() {
	/// called when rewarding honor and at each save
	time_t now = time_t(time(NULL));
	time_t today = time_t(time(NULL) / DAY) * DAY;

	if (m_lastHonorUpdateTime < today) {
		time_t yesterday = today - DAY;

		uint16 kills_today = PAIR32_LOPART(GetUInt32Value(PLAYER_FIELD_KILLS));

		// update yesterday's contribution
		if (m_lastHonorUpdateTime >= yesterday) {
			//SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION));

			// this is the first update today, reset today's contribution
			//SetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, 0);
			SetUInt32Value(PLAYER_FIELD_KILLS, MAKE_PAIR32(0, kills_today));
		} else {
			// no honor/kills yesterday or today, reset
			//SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0);
			SetUInt32Value(PLAYER_FIELD_KILLS, 0);
		}
	}

	m_lastHonorUpdateTime = now;
}

///Calculate the amount of honor gained based on the victim
///and the size of the group for which the honor is divided
///An exact honor value can also be given (overriding the calcs)
bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, int32 honor,
		bool pvptoken) {
	// do not reward honor in arenas, but enable onkill spellproc
	if (InArena()) {
		if (!uVictim || uVictim == this
				|| uVictim->GetTypeId() != TYPEID_PLAYER)
			return false;

		if (GetBGTeam() == uVictim->ToPlayer()->GetBGTeam())
			return false;

		return true;
	}

	// 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
	if (HasAura(SPELL_AURA_PLAYER_INACTIVE))
		return false;

	uint64 victim_guid = 0;
	uint32 victim_rank = 0;

	// need call before fields update to have chance move yesterday data to appropriate fields before today data change.
	UpdateHonorFields();

	// do not reward honor in arenas, but return true to enable onkill spellproc
	if (InBattleground() && GetBattleground() && GetBattleground()->isArena())
		return true;

	// Promote to float for calculations
	float honor_f = (float) honor;

	if (honor_f <= 0) {
		if (!uVictim || uVictim == this
				|| uVictim->HasAuraType(SPELL_AURA_NO_PVP_CREDIT))
			return false;

		victim_guid = uVictim->GetGUID();

		if (uVictim->GetTypeId() == TYPEID_PLAYER) {
			Player *pVictim = uVictim->ToPlayer();

			if (GetTeam() == pVictim->GetTeam() && !sWorld->IsFFAPvPRealm())
				return false;

			uint8 k_level = getLevel();
			uint8 k_grey = Trinity::XP::GetGrayLevel(k_level);
			uint8 v_level = pVictim->getLevel();

			if (v_level <= k_grey)
				return false;

			// PLAYER_CHOSEN_TITLE VALUES DESCRIPTION
			//  [0]      Just name
			//  [1..14]  Alliance honor titles and player name
			//  [15..28] Horde honor titles and player name
			//  [29..38] Other title and player name
			//  [39+]    Nothing
			uint32 victim_title = pVictim->GetUInt32Value(PLAYER_CHOSEN_TITLE);
			// Get Killer titles, CharTitlesEntry::bit_index
			// Ranks:
			//  title[1..14]  -> rank[5..18]
			//  title[15..28] -> rank[5..18]
			//  title[other]  -> 0
			if (victim_title == 0)
				victim_guid = 0; // Don't show HK: <rank> message, only log.
			else if (victim_title < 15)
				victim_rank = victim_title + 4;
			else if (victim_title < 29)
				victim_rank = victim_title - 14 + 4;
			else
				victim_guid = 0; // Don't show HK: <rank> message, only log.

			honor_f = ceil(
					Trinity::Honor::hk_honor_at_level_f(k_level)
							* (v_level - k_grey) / (k_level - k_grey));

			// count the number of playerkills in one day
			ApplyModUInt32Value(PLAYER_FIELD_KILLS, 1, true);
			// and those in a lifetime
			//ApplyModUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, 1, true);
			UpdateAchievementCriteria(
					ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL);
			UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS,
					pVictim->getClass());
			UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HK_RACE,
					pVictim->getRace());
			UpdateAchievementCriteria(
					ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA,
					GetAreaId());
			UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL,
					1, 0, pVictim);
		} else {
			if (!uVictim->ToCreature()->isRacialLeader())
				return false;

			honor_f = 100.0f; // ??? need more info
			victim_rank = 19; // HK: Leader
		}
	}

	if (uVictim != NULL) {
		if (groupsize > 1)
			honor_f /= groupsize;

		// apply honor multiplier from aura (not stacking-get highest)
		honor_f *= (GetMaxPositiveAuraModifier(SPELL_AURA_MOD_HONOR_GAIN_PCT)
				+ 100) / 100.0f;
	}

	honor_f *= sWorld->getRate(RATE_HONOR);
	// Back to int now
	honor = int32(honor_f);
	// honor - for show honor points in log
	// victim_guid - for show victim name in log
	// victim_rank [1..4]  HK: <dishonored rank>
	// victim_rank [5..19] HK: <alliance\horde rank>
	// victim_rank [0, 20+] HK: <>
	WorldPacket data(SMSG_PVP_CREDIT, 4 + 8 + 4);
	data << honor;
	data << victim_guid;
	data << victim_rank;

	GetSession()->SendPacket(&data);

	// add honor points
	ModifyCurrency(CURRENCY_TYPE_HONOR_POINTS, int32(honor) * 2.4);

	//ApplyModUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, honor, true);

	if (InBattleground() && honor > 0) {
		if (Battleground *bg = GetBattleground()) {
			bg->UpdatePlayerScore(this, SCORE_BONUS_HONOR, honor, false); //false: prevent looping
		}
	}

	if (sWorld->getBoolConfig(CONFIG_PVP_TOKEN_ENABLE) && pvptoken) {
		if (!uVictim || uVictim == this
				|| uVictim->HasAuraType(SPELL_AURA_NO_PVP_CREDIT))
			return true;

		if (uVictim->GetTypeId() == TYPEID_PLAYER) {
			// Check if allowed to receive it in current map
			uint8 MapType = sWorld->getIntConfig(CONFIG_PVP_TOKEN_MAP_TYPE);
			if ((MapType == 1 && !InBattleground()
					&& !HasByteFlag(UNIT_FIELD_BYTES_2, 1,
							UNIT_BYTE2_FLAG_FFA_PVP))
					|| (MapType == 2
							&& !HasByteFlag(UNIT_FIELD_BYTES_2, 1,
									UNIT_BYTE2_FLAG_FFA_PVP))
					|| (MapType == 3 && !InBattleground()))
				return true;

			uint32 itemId = sWorld->getIntConfig(CONFIG_PVP_TOKEN_ID);
			int32 count = sWorld->getIntConfig(CONFIG_PVP_TOKEN_COUNT);

			if (AddItem(itemId, count))
				ChatHandler(this).PSendSysMessage(
						"You have been awarded a token for slaying another player.");
		}
	}

	return true;
}

uint32 Player::GetGuildIdFromDB(uint64 guid) {
	QueryResult result = CharacterDatabase.PQuery(
			"SELECT guildid FROM guild_member WHERE guid='%u'",
			GUID_LOPART(guid));
	if (!result)
		return 0;

	uint32 id = result->Fetch()[0].GetUInt32();
	return id;
}

uint8 Player::GetRankFromDB(uint64 guid) {
	QueryResult result = CharacterDatabase.PQuery(
			"SELECT rank FROM guild_member WHERE guid='%u'", GUID_LOPART(guid));
	if (result) {
		uint32 v = result->Fetch()[0].GetUInt8();
		return v;
	} else
		return 0;
}

uint32 Player::GetArenaTeamIdFromDB(uint64 guid, uint8 type) {
	QueryResult result =
			CharacterDatabase.PQuery(
					"SELECT arena_team_member.arenateamid FROM arena_team_member JOIN arena_team ON arena_team_member.arenateamid = arena_team.arenateamid WHERE guid='%u' AND type='%u' LIMIT 1",
					GUID_LOPART(guid), type);
	if (!result)
		return 0;

	uint32 id = (*result)[0].GetUInt32();
	return id;
}

uint32 Player::GetZoneIdFromDB(uint64 guid) {
	uint32 guidLow = GUID_LOPART(guid);
	QueryResult result = CharacterDatabase.PQuery(
			"SELECT zone FROM characters WHERE guid='%u'", guidLow);
	if (!result)
		return 0;
	Field* fields = result->Fetch();
	uint32 zone = fields[0].GetUInt16();

	if (!zone) {
		// stored zone is zero, use generic and slow zone detection
		result =
				CharacterDatabase.PQuery(
						"SELECT map, position_x, position_y, position_z FROM characters WHERE guid='%u'",
						guidLow);
		if (!result)
			return 0;
		fields = result->Fetch();
		uint32 map = fields[0].GetUInt16();
		float posx = fields[1].GetFloat();
		float posy = fields[2].GetFloat();
		float posz = fields[3].GetFloat();

		zone = sMapMgr->GetZoneId(map, posx, posy, posz);

		if (zone > 0)
			CharacterDatabase.PExecute(
					"UPDATE characters SET zone='%u' WHERE guid='%u'", zone,
					guidLow);
	}

	return zone;
}

uint32 Player::GetLevelFromDB(uint64 guid) {
	QueryResult result = CharacterDatabase.PQuery(
			"SELECT level FROM characters WHERE guid='%u'", GUID_LOPART(guid));
	if (!result)
		return 0;

	Field* fields = result->Fetch();
	uint8 level = fields[0].GetUInt8();

	return level;
}

void Player::UpdateArea(uint32 newArea) {
	// FFA_PVP flags are area and not zone id dependent
	// so apply them accordingly
	m_areaUpdateId = newArea;

	AreaTableEntry const* area = GetAreaEntryByAreaID(newArea);
	pvpInfo.inFFAPvPArea = area && (area->flags & AREA_FLAG_ARENA);
	UpdatePvPState(true);

	UpdateAreaDependentAuras(newArea);
}

void Player::UpdateZone(uint32 newZone, uint32 newArea) {
	if (m_zoneUpdateId != newZone) {
		if (Guild * pGuild = sObjectMgr->GetGuildById(GetGuildId()))
			pGuild->UpdateMemberData(this, GUILD_MEMBER_DATA_ZONEID, newZone);

		sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
		sOutdoorPvPMgr->HandlePlayerEnterZone(this, newZone);
		SendInitWorldStates(newZone, newArea); // only if really enters to new zone, not just area change, works strange...
		
        // zone changed, check mount
        bool allowMount = false;
        if (InstanceTemplate const* mInstance = sObjectMgr->GetInstanceTemplate(GetMapId()))
            allowMount = mInstance->allowMount;
        else if (MapEntry const* mEntry = sMapStore.LookupEntry(GetMapId()))
            allowMount = !mEntry->IsDungeon() || mEntry->IsBattlegroundOrArena();

        if (!allowMount)
        {
            RemoveAurasByType(SPELL_AURA_MOUNTED);

            if (IsInDisallowedMountForm())
                RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
        }
		
	}

	m_zoneUpdateId = newZone;
	m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;

	// zone changed, so area changed as well, update it
	UpdateArea(newArea);

	AreaTableEntry const* zone = GetAreaEntryByAreaID(newZone);
	if (!zone)
		return;

	if (sWorld->getBoolConfig(CONFIG_WEATHER)) {
		Weather *wth = sWeatherMgr->FindWeather(zone->ID);
		if (wth)
			wth->SendWeatherUpdateToPlayer(this);
		else {
			if (!sWeatherMgr->AddWeather(zone->ID)) {
				// send fine weather packet to remove old zone's weather
				Weather::SendFineWeatherUpdateToPlayer(this);
			}
		}
	}

	// in PvP, any not controlled zone (except zone->team == 6, default case)
	// in PvE, only opposition team capital
	switch (zone->team) {
	case AREATEAM_ALLY:
		pvpInfo.inHostileArea = GetTeam() != ALLIANCE
				&& (sWorld->IsPvPRealm() || zone->flags & AREA_FLAG_CAPITAL);
		break;
	case AREATEAM_HORDE:
		pvpInfo.inHostileArea = GetTeam() != HORDE
				&& (sWorld->IsPvPRealm() || zone->flags & AREA_FLAG_CAPITAL);
		break;
	case AREATEAM_NONE:
		// overwrite for battlegrounds, maybe batter some zone flags but current known not 100% fit to this
		pvpInfo.inHostileArea = sWorld->IsPvPRealm() || InBattleground()
				|| zone->flags & AREA_FLAG_OUTDOOR_PVP;
		break;
	default: // 6 in fact
		pvpInfo.inHostileArea = false;
		break;
	}

	pvpInfo.inNoPvPArea = false;
	if (zone->IsSanctuary()) // in sanctuary
	{
		SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY);
		pvpInfo.inNoPvPArea = true;
		CombatStopWithPets();
	} else
		RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY);

	if (zone->flags & AREA_FLAG_CAPITAL) // in capital city
			{
		if (!pvpInfo.inHostileArea || zone->IsSanctuary()) {
			SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
			SetRestType(REST_TYPE_IN_CITY);
			InnEnter(time(0), GetMapId(), 0, 0, 0);
		}
		pvpInfo.inNoPvPArea = true;
	} else // anywhere else
	{
		if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING)) // but resting (walk from city or maybe in tavern or leave tavern recently)
				{
			if (GetRestType() == REST_TYPE_IN_TAVERN) // has been in tavern. Is still in?
					{
				if (GetMapId() != GetInnPosMapId()
						|| sqrt(
								(GetPositionX() - GetInnPosX())
										* (GetPositionX() - GetInnPosX())
										+ (GetPositionY() - GetInnPosY())
												* (GetPositionY() - GetInnPosY())
										+ (GetPositionZ() - GetInnPosZ())
												* (GetPositionZ() - GetInnPosZ()))
								> 40) {
					RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
					SetRestType(REST_TYPE_NO);
				}
			} else // not in tavern (leave city then)
			{
				RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
				SetRestType(REST_TYPE_NO);
			}
		}
	}

	UpdatePvPState();

	// remove items with area/map limitations (delete only for alive player to allow back in ghost mode)
	// if player resurrected at teleport this will be applied in resurrect code
	if (isAlive())
		DestroyZoneLimitedItem(true, newZone);

	// check some item equip limitations (in result lost CanTitanGrip at talent reset, for example)
	AutoUnequipOffhandIfNeed();

	// recent client version not send leave/join channel packets for built-in local channels
	UpdateLocalChannels(newZone);

	// group update
	if (GetGroup())
		SetGroupUpdateFlag(GROUP_UPDATE_FLAG_ZONE);

	UpdateZoneDependentAuras(newZone);
}

//If players are too far way of duel flag... then player loose the duel
void Player::CheckDuelDistance(time_t currTime) {
	if (!duel)
		return;

	uint64 duelFlagGUID = GetUInt64Value(PLAYER_DUEL_ARBITER);
	GameObject* obj = GetMap()->GetGameObject(duelFlagGUID);
	if (!obj)
		return;

	if (duel->outOfBound == 0) {
		if (!IsWithinDistInMap(obj, 50)) {
			duel->outOfBound = currTime;

			WorldPacket data(SMSG_DUEL_OUTOFBOUNDS, 0);
			GetSession()->SendPacket(&data);
		}
	} else {
		if (IsWithinDistInMap(obj, 40)) {
			duel->outOfBound = 0;

			WorldPacket data(SMSG_DUEL_INBOUNDS, 0);
			GetSession()->SendPacket(&data);
		} else if (currTime >= (duel->outOfBound + 10))
			DuelComplete(DUEL_FLED);
	}
}

bool Player::IsOutdoorPvPActive() {
	return isAlive() && !HasInvisibilityAura() && !HasStealthAura()
			&& (IsPvP() || sWorld->IsPvPRealm())
			&& !HasUnitMovementFlag(MOVEMENTFLAG_FLYING) && !isInFlight();
}

void Player::DuelComplete(DuelCompleteType type) {
	// duel not requested
	if (!duel)
		return;

	sLog->outDebug(LOG_FILTER_UNITS, "Duel Complete %s %s", GetName(),
			duel->opponent->GetName());

	WorldPacket data(SMSG_DUEL_COMPLETE, (1), true);
	data << (uint8) ((type != DUEL_INTERRUPTED) ? 1 : 0);
	GetSession()->SendPacket(&data);

	if (duel->opponent->GetSession())
		duel->opponent->GetSession()->SendPacket(&data);

	if (type != DUEL_INTERRUPTED) {
		data.Initialize(SMSG_DUEL_WINNER, (1 + 20), true); // we guess size
		data << uint8(type == DUEL_WON ? 0 : 1); // 0 = just won; 1 = fled
		data << duel->opponent->GetName();
		data << GetName();
		SendMessageToSet(&data, true);
	}

	sScriptMgr->OnPlayerDuelEnd(duel->opponent, this, type);

	switch (type) {
	case DUEL_FLED:
		// if initiator and opponent are on the same team
		// or initiator and opponent are not PvP enabled, forcibly stop attacking
		if (duel->initiator->GetTeam() == duel->opponent->GetTeam()) {
			duel->initiator->AttackStop();
			duel->opponent->AttackStop();
		} else {
			if (!duel->initiator->IsPvP())
				duel->initiator->AttackStop();
			if (!duel->opponent->IsPvP())
				duel->opponent->AttackStop();
		}
		break;
	case DUEL_WON:
		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL, 1);
		if (duel->opponent) {
			duel->opponent->GetAchievementMgr().UpdateAchievementCriteria(
					ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL, 1);
			duel->opponent->HandleEmoteCommand(EMOTE_ONESHOT_CHEER);

			//Credit for quest Death's Challenge
			if (getClass() == CLASS_DEATH_KNIGHT
					&& duel->opponent->GetQuestStatus(12733)
							== QUEST_STATUS_INCOMPLETE)
				duel->opponent->CastSpell(duel->opponent, 52994, true);
		}
		break;
	default:
		break;
	}

	//Remove Duel Flag object
	GameObject* obj = GetMap()->GetGameObject(
			GetUInt64Value(PLAYER_DUEL_ARBITER));
	if (obj)
		duel->initiator->RemoveGameObject(obj, true);

	/* remove auras */
	AuraApplicationMap &itsAuras = duel->opponent->GetAppliedAuras();
	for (AuraApplicationMap::iterator i = itsAuras.begin(); i != itsAuras.end();
			) {
		Aura const * aura = i->second->GetBase();
		if (!i->second->IsPositive() && aura->GetCasterGUID() == GetGUID()
				&& aura->GetApplyTime() >= duel->startTime)
			duel->opponent->RemoveAura(i);
		else
			++i;
	}

	AuraApplicationMap &myAuras = GetAppliedAuras();
	for (AuraApplicationMap::iterator i = myAuras.begin(); i != myAuras.end();
			) {
		Aura const * aura = i->second->GetBase();
		if (!i->second->IsPositive()
				&& aura->GetCasterGUID() == duel->opponent->GetGUID()
				&& aura->GetApplyTime() >= duel->startTime)
			RemoveAura(i);
		else
			++i;
	}

	// cleanup combo points
	if (GetComboTarget() == duel->opponent->GetGUID())
		ClearComboPoints();
	else if (GetComboTarget() == duel->opponent->GetPetGUID())
		ClearComboPoints();

	if (duel->opponent->GetComboTarget() == GetGUID())
		duel->opponent->ClearComboPoints();
	else if (duel->opponent->GetComboTarget() == GetPetGUID())
		duel->opponent->ClearComboPoints();

	// Honor points after duel (the winner) - ImpConfig
	if (uint32 amount = sWorld->getIntConfig(CONFIG_HONOR_AFTER_DUEL))
		duel->opponent->RewardHonor(NULL, 1, amount);

	//cleanups
	SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
	SetUInt32Value(PLAYER_DUEL_TEAM, 0);
	duel->opponent->SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
	duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 0);

	delete duel->opponent->duel;
	duel->opponent->duel = NULL;
	delete duel;
	duel = NULL;
}

//---------------------------------------------------------//

void Player::_ApplyItemMods(Item *item, uint8 slot, bool apply) {
	if (slot >= INVENTORY_SLOT_BAG_END || !item)
		return;

	ItemPrototype const *proto = item->GetProto();

	if (!proto)
		return;

	// not apply/remove mods for broken item
	if (item->IsBroken())
		return;

	sLog->outDetail("applying mods for item %u ", item->GetGUIDLow());

	uint8 attacktype = Player::GetAttackBySlot(slot);

	if (proto->Socket[0].Color) //only (un)equipping of items with sockets can influence metagems, so no need to waste time with normal items
		CorrectMetaGemEnchants(slot, apply);

	if (attacktype < MAX_ATTACK)
		_ApplyWeaponDependentAuraMods(item, WeaponAttackType(attacktype),
				apply);

	_ApplyItemBonuses(proto, slot, apply);

	if (slot == EQUIPMENT_SLOT_RANGED)
		_ApplyAmmoBonuses();

	ApplyItemEquipSpell(item, apply);
	ApplyEnchantment(item, apply);

	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "_ApplyItemMods complete.");
}

void Player::_ApplyItemBonuses(ItemPrototype const *proto, uint8 slot,
		bool apply, bool only_level_scale /*= false*/) {
	if (slot >= INVENTORY_SLOT_BAG_END || !proto)
		return;

	ScalingStatDistributionEntry const *ssd =
			proto->ScalingStatDistribution ?
					sScalingStatDistributionStore.LookupEntry(
							proto->ScalingStatDistribution) :
					NULL;
	if (only_level_scale && !ssd)
		return;

	// req. check at equip, but allow use for extended range if range limit max level, set proper level
	uint32 ssd_level = getLevel();
	if (ssd && ssd_level > ssd->MaxLevel)
		ssd_level = ssd->MaxLevel;

	ScalingStatValuesEntry const *ssv =
			proto->ScalingStatValue ?
					sScalingStatValuesStore.LookupEntry(ssd_level) : NULL;
	if (only_level_scale && !ssv)
		return;

	for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i) {
		uint32 statType = 0;
		int32 val = 0;
		// If set ScalingStatDistribution need get stats and values from it
		if (ssd && ssv) {
			if (ssd->StatMod[i] < 0)
				continue;
			statType = ssd->StatMod[i];
			val = (ssv->getssdMultiplier(proto->ScalingStatValue)
					* ssd->Modifier[i]) / 10000;
		} else {
			if (proto->ItemStat[i].ItemStatValue == 0)
				continue;
			statType = proto->ItemStat[i].ItemStatType;
			val = proto->ItemStat[i].ItemStatValue;
		}

		if (val == 0)
			continue;

		switch (statType) {
		case ITEM_MOD_MANA:
			HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(val), apply);
			break;
		case ITEM_MOD_HEALTH: // modify HP
			HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(val), apply);
			break;
		case ITEM_MOD_AGILITY: // modify agility
			HandleStatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val),
					apply);
			ApplyStatBuffMod(STAT_AGILITY, float(val), apply);
			break;
		case ITEM_MOD_STRENGTH: //modify strength
			HandleStatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val),
					apply);
			ApplyStatBuffMod(STAT_STRENGTH, float(val), apply);
			break;
		case ITEM_MOD_INTELLECT: //modify intellect
			HandleStatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val),
					apply);
			ApplyStatBuffMod(STAT_INTELLECT, float(val), apply);
			break;
		case ITEM_MOD_SPIRIT: //modify spirit
			HandleStatModifier(UNIT_MOD_STAT_SPIRIT, BASE_VALUE, float(val),
					apply);
			ApplyStatBuffMod(STAT_SPIRIT, float(val), apply);
			break;
		case ITEM_MOD_STAMINA: //modify stamina
			HandleStatModifier(UNIT_MOD_STAT_STAMINA, BASE_VALUE, float(val),
					apply);
			ApplyStatBuffMod(STAT_STAMINA, float(val), apply);
			break;
		case ITEM_MOD_DEFENSE_SKILL_RATING:
			ApplyRatingMod(CR_DEFENSE_SKILL, int32(val), apply);
			break;
		case ITEM_MOD_DODGE_RATING:
			ApplyRatingMod(CR_DODGE, int32(val), apply);
			break;
		case ITEM_MOD_PARRY_RATING:
			ApplyRatingMod(CR_PARRY, int32(val), apply);
			break;
		case ITEM_MOD_BLOCK_RATING:
			ApplyRatingMod(CR_BLOCK, int32(val), apply);
			break;
		case ITEM_MOD_HIT_MELEE_RATING:
			ApplyRatingMod(CR_HIT_MELEE, int32(val), apply);
			break;
		case ITEM_MOD_HIT_RANGED_RATING:
			ApplyRatingMod(CR_HIT_RANGED, int32(val), apply);
			break;
		case ITEM_MOD_HIT_SPELL_RATING:
			ApplyRatingMod(CR_HIT_SPELL, int32(val), apply);
			break;
		case ITEM_MOD_CRIT_MELEE_RATING:
			ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply);
			break;
		case ITEM_MOD_CRIT_RANGED_RATING:
			ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply);
			break;
		case ITEM_MOD_CRIT_SPELL_RATING:
			ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply);
			break;
		case ITEM_MOD_HIT_TAKEN_MELEE_RATING:
			ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply);
			break;
		case ITEM_MOD_HIT_TAKEN_RANGED_RATING:
			ApplyRatingMod(CR_HIT_TAKEN_RANGED, int32(val), apply);
			break;
		case ITEM_MOD_HIT_TAKEN_SPELL_RATING:
			ApplyRatingMod(CR_HIT_TAKEN_SPELL, int32(val), apply);
			break;
		case ITEM_MOD_CRIT_TAKEN_MELEE_RATING:
			//ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
			break;
		case ITEM_MOD_CRIT_TAKEN_RANGED_RATING:
			//ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
			break;
		case ITEM_MOD_CRIT_TAKEN_SPELL_RATING:
			ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
			break;
		case ITEM_MOD_HASTE_MELEE_RATING:
			ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply);
			break;
		case ITEM_MOD_HASTE_RANGED_RATING:
			ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply);
			break;
		case ITEM_MOD_HASTE_SPELL_RATING:
			ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply);
			break;
		case ITEM_MOD_HIT_RATING:
			ApplyRatingMod(CR_HIT_MELEE, int32(val), apply);
			ApplyRatingMod(CR_HIT_RANGED, int32(val), apply);
			ApplyRatingMod(CR_HIT_SPELL, int32(val), apply);
			break;
		case ITEM_MOD_CRIT_RATING:
			ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply);
			ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply);
			ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply);
			break;
		case ITEM_MOD_HIT_TAKEN_RATING:
			ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply);
			ApplyRatingMod(CR_HIT_TAKEN_RANGED, int32(val), apply);
			ApplyRatingMod(CR_HIT_TAKEN_SPELL, int32(val), apply);
			break;
		case ITEM_MOD_CRIT_TAKEN_RATING:
			/*ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
			 ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
			 ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);*/
			break;
		case ITEM_MOD_RESILIENCE_RATING:
			ApplyRatingMod(CR_RESILIENCE_PLAYER_DAMAGE_TAKEN, int32(val),
					apply);
			break;
		case ITEM_MOD_HASTE_RATING:
			ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply);
			ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply);
			ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply);
			break;
		case ITEM_MOD_EXPERTISE_RATING:
			ApplyRatingMod(CR_EXPERTISE, int32(val), apply);
			break;
		case ITEM_MOD_ATTACK_POWER:
			if (float(val) > 0.f) {
				HandleStatModifier(UNIT_MOD_ATTACK_POWER_POS, TOTAL_VALUE,
						float(val), apply);
				HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED_POS,
						TOTAL_VALUE, float(val), apply);
			} else {
				HandleStatModifier(UNIT_MOD_ATTACK_POWER_NEG, TOTAL_VALUE,
						-float(val), apply);
				HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED_NEG,
						TOTAL_VALUE, -float(val), apply);
			}
			break;
		case ITEM_MOD_RANGED_ATTACK_POWER:
			if (float(val) > 0.f)
				HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED_POS,
						TOTAL_VALUE, float(val), apply);
			else
				HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED_NEG,
						TOTAL_VALUE, -float(val), apply);
			break;
//            case ITEM_MOD_FERAL_ATTACK_POWER:
//                ApplyFeralAPBonus(int32(val), apply);
//                break;
		case ITEM_MOD_MANA_REGENERATION:
			ApplyManaRegenBonus(int32(val), apply);
			break;
		case ITEM_MOD_ARMOR_PENETRATION_RATING:
			ApplyRatingMod(CR_ARMOR_PENETRATION, int32(val), apply);
			break;
		case ITEM_MOD_SPELL_POWER:
			ApplySpellPowerBonus(int32(val), apply);
			break;
		case ITEM_MOD_HEALTH_REGEN:
			ApplyHealthRegenBonus(int32(val), apply);
			break;
		case ITEM_MOD_SPELL_PENETRATION:
			ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE, -val, apply);
			m_spellPenetrationItemMod += apply ? val : -val;
			break;
			// deprecated item mods
		case ITEM_MOD_SPELL_HEALING_DONE:
		case ITEM_MOD_SPELL_DAMAGE_DONE:
			break;
		case ITEM_MOD_MASTERY_RATING:
			ApplyRatingMod(CR_MASTERY, int32(val), apply);
			break;
		}
	}

	// Apply Spell Power from ScalingStatValue if set
	if (ssv) {
		if (int32 spellbonus = ssv->getSpellBonus(proto->ScalingStatValue))
			ApplySpellPowerBonus(spellbonus, apply);
	}

	// If set ScalingStatValue armor get it or use item armor
	uint32 armor = proto->GetArmor();
	if (ssv) {
		if (uint32 ssvarmor = ssv->getArmorMod(proto->ScalingStatValue))
			armor = ssvarmor;
	}

	if (armor) {
		UnitModifierType modType = TOTAL_VALUE;
		if (proto->Class == ITEM_CLASS_ARMOR) {
			switch (proto->SubClass) {
			case ITEM_SUBCLASS_ARMOR_CLOTH:
			case ITEM_SUBCLASS_ARMOR_LEATHER:
			case ITEM_SUBCLASS_ARMOR_MAIL:
			case ITEM_SUBCLASS_ARMOR_PLATE:
			case ITEM_SUBCLASS_ARMOR_SHIELD:
				modType = BASE_VALUE;
				break;
			}
		}
		HandleStatModifier(UNIT_MOD_ARMOR, modType, float(armor), apply);
	}

	// Add armor bonus from ArmorDamageModifier if > 0
	if (proto->ArmorDamageModifier > 0)
		HandleStatModifier(UNIT_MOD_ARMOR, TOTAL_VALUE,
				float(proto->ArmorDamageModifier), apply);

	WeaponAttackType attType = BASE_ATTACK;

	if (slot == EQUIPMENT_SLOT_RANGED
			&& (proto->InventoryType == INVTYPE_RANGED
					|| proto->InventoryType == INVTYPE_THROWN
					|| proto->InventoryType == INVTYPE_RANGEDRIGHT)) {
		attType = RANGED_ATTACK;
	} else if (slot == EQUIPMENT_SLOT_OFFHAND) {
		attType = OFF_ATTACK;
	}

	if (CanUseAttackType(attType))
		_ApplyWeaponDamage(slot, proto, ssv, apply);

	int32 extraDPS = ssv->getDPSMod(proto->ScalingStatValue);

	// Apply feral bonus from ScalingStatValue if set
	if (ssv) {
		if (int32 feral_bonus = ssv->getFeralBonus(proto->ScalingStatValue))
			ApplyFeralAPBonus(feral_bonus, apply);
	}
	// Druids get feral AP bonus from weapon dps (lso use DPS from ScalingStatValue)
	if (getClass() == CLASS_DRUID) {
		int32 feral_bonus = proto->getFeralBonus(extraDPS);
		if (feral_bonus > 0)
			ApplyFeralAPBonus(feral_bonus, apply);
	}
}

void Player::_ApplyWeaponDamage(uint8 slot, ItemPrototype const *proto,
		ScalingStatValuesEntry const *ssv, bool apply) {
	WeaponAttackType attType = BASE_ATTACK;
	float damage = 0.0f;

	if (slot == EQUIPMENT_SLOT_RANGED
			&& (proto->InventoryType == INVTYPE_RANGED
					|| proto->InventoryType == INVTYPE_THROWN
					|| proto->InventoryType == INVTYPE_RANGEDRIGHT)) {
		attType = RANGED_ATTACK;
	} else if (slot == EQUIPMENT_SLOT_OFFHAND) {
		attType = OFF_ATTACK;
	}

	float minDamage = proto->GetMinDamage();
	float maxDamage = proto->GetMaxDamage();

	// If set dpsMod in ScalingStatValue use it for min (70% from average), max (130% from average) damage
	if (ssv) {
		int32 extraDPS = ssv->getDPSMod(proto->ScalingStatValue);
		if (extraDPS) {
			float average = extraDPS * proto->Delay / 1000.0f;
			minDamage = 0.7f * average;
			maxDamage = 1.3f * average;
		}
	}

	if (minDamage > 0) {
		damage = apply ? minDamage : BASE_MINDAMAGE;
		SetBaseWeaponDamage(attType, MINDAMAGE, damage);
	}

	if (maxDamage > 0) {
		damage = apply ? maxDamage : BASE_MAXDAMAGE;
		SetBaseWeaponDamage(attType, MAXDAMAGE, damage);
	}

	if (proto->Delay && !IsInFeralForm()) {
		if (slot == EQUIPMENT_SLOT_RANGED)
			SetAttackTime(RANGED_ATTACK,
					apply ? proto->Delay : BASE_ATTACK_TIME);
		else if (slot == EQUIPMENT_SLOT_MAINHAND)
			SetAttackTime(BASE_ATTACK, apply ? proto->Delay : BASE_ATTACK_TIME);
		else if (slot == EQUIPMENT_SLOT_OFFHAND)
			SetAttackTime(OFF_ATTACK, apply ? proto->Delay : BASE_ATTACK_TIME);
	}

	// No need to modify any physical damage for ferals as it is calculated from stats only
	if (IsInFeralForm())
		return;

	if (CanModifyStats() && (damage || proto->Delay))
		UpdateDamagePhysical(attType);
}

void Player::_ApplyWeaponDependentAuraMods(Item *item,
		WeaponAttackType attackType, bool apply) {
	AuraEffectList const& auraCritList = GetAuraEffectsByType(
			SPELL_AURA_MOD_WEAPON_CRIT_PERCENT);
	for (AuraEffectList::const_iterator itr = auraCritList.begin();
			itr != auraCritList.end(); ++itr)
		_ApplyWeaponDependentAuraCritMod(item, attackType, *itr, apply);

	AuraEffectList const& auraDamageFlatList = GetAuraEffectsByType(
			SPELL_AURA_MOD_DAMAGE_DONE);
	for (AuraEffectList::const_iterator itr = auraDamageFlatList.begin();
			itr != auraDamageFlatList.end(); ++itr)
		_ApplyWeaponDependentAuraDamageMod(item, attackType, *itr, apply);

	AuraEffectList const& auraDamagePCTList = GetAuraEffectsByType(
			SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
	for (AuraEffectList::const_iterator itr = auraDamagePCTList.begin();
			itr != auraDamagePCTList.end(); ++itr)
		_ApplyWeaponDependentAuraDamageMod(item, attackType, *itr, apply);

	float mod = 100.0f;
	AuraEffectList const& auraDamagePctList = GetAuraEffectsByType(
			SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
	for (AuraEffectList::const_iterator itr = auraDamagePctList.begin();
			itr != auraDamagePctList.end(); ++itr)
		if ((apply && item->IsFitToSpellRequirements((*itr)->GetSpellProto()))
				|| HasItemFitToSpellRequirements((*itr)->GetSpellProto(), item))
			mod += (*itr)->GetAmount();

	SetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT, mod / 100.0f);
}

void Player::_ApplyWeaponDependentAuraCritMod(Item *item,
		WeaponAttackType attackType, AuraEffect const* aura, bool apply) {
	// generic not weapon specific case processes in aura code
	if (aura->GetSpellProto()->EquippedItemClass == -1)
		return;

	BaseModGroup mod = BASEMOD_END;
	switch (attackType) {
	case BASE_ATTACK:
		mod = CRIT_PERCENTAGE;
		break;
	case OFF_ATTACK:
		mod = OFFHAND_CRIT_PERCENTAGE;
		break;
	case RANGED_ATTACK:
		mod = RANGED_CRIT_PERCENTAGE;
		break;
	default:
		return;
	}

	if (!item->IsBroken()
			&& item->IsFitToSpellRequirements(aura->GetSpellProto()))
		HandleBaseModValue(mod, FLAT_MOD, float(aura->GetAmount()), apply);
}

void Player::_ApplyWeaponDependentAuraDamageMod(Item *item,
		WeaponAttackType attackType, AuraEffect const* aura, bool apply) {
	//don't apply mod if item is broken
	if (item->IsBroken() || !CanUseAttackType(attackType))
		return;

	// ignore spell mods for not wands
	if ((aura->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) == 0
			&& (getClassMask() & CLASSMASK_WAND_USERS) == 0)
		return;

	// generic not weapon specific case processes in aura code
	if (aura->GetSpellProto()->EquippedItemClass == -1)
		return;

	UnitMods unitMod = UNIT_MOD_END;
	switch (attackType) {
	case BASE_ATTACK:
		unitMod = UNIT_MOD_DAMAGE_MAINHAND;
		break;
	case OFF_ATTACK:
		unitMod = UNIT_MOD_DAMAGE_OFFHAND;
		break;
	case RANGED_ATTACK:
		unitMod = UNIT_MOD_DAMAGE_RANGED;
		break;
	default:
		return;
	}

	UnitModifierType unitModType = TOTAL_VALUE;
	switch (aura->GetAuraType()) {
	case SPELL_AURA_MOD_DAMAGE_DONE:
		unitModType = TOTAL_VALUE;
		break;
	case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE:
		unitModType = TOTAL_PCT;
		break;
	default:
		return;
	}

	if (item->IsFitToSpellRequirements(aura->GetSpellProto())) {
		HandleStatModifier(unitMod, unitModType, float(aura->GetAmount()),
				apply);
		ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS, aura->GetAmount(),
				apply);

		if (unitModType == TOTAL_PCT)
			ApplyModSignedFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT,
					aura->GetAmount() / 100.0f, apply);
		else
			ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS,
					aura->GetAmount(), apply);
	}
}

void Player::ApplyItemEquipSpell(Item *item, bool apply, bool form_change) {
	if (!item)
		return;

	ItemPrototype const *proto = item->GetProto();
	if (!proto)
		return;

	for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) {
		_Spell const& spellData = proto->Spells[i];

		// no spell
		if (!spellData.SpellId)
			continue;

		// wrong triggering type
		if (apply && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_EQUIP)
			continue;

		// check if it is valid spell
		SpellEntry const* spellproto = sSpellStore.LookupEntry(
				spellData.SpellId);
		if (!spellproto)
			continue;

		ApplyEquipSpell(spellproto, item, apply, form_change);
	}
}

void Player::ApplyEquipSpell(SpellEntry const* spellInfo, Item* item,
		bool apply, bool form_change) {
	if (apply) {
		// Cannot be used in this stance/form
		if (GetErrorAtShapeshiftedCast(spellInfo, GetShapeshiftForm())
				!= SPELL_CAST_OK)
			return;

		if (form_change) // check aura active state from other form
		{
			AuraApplicationMap const& auras = GetAppliedAuras();
			for (AuraApplicationMap::const_iterator itr = auras.lower_bound(
					spellInfo->Id); itr != auras.upper_bound(spellInfo->Id);
					++itr)
				if (!item
						|| itr->second->GetBase()->GetCastItemGUID()
								== item->GetGUID())
					return;
		}

		sLog->outStaticDebug("WORLD: cast %s Equip spellId - %i",
				(item ? "item" : "itemset"), spellInfo->Id);

		CastSpell(this, spellInfo, true, item);
	} else {
		if (form_change) // check aura compatibility
		{
			// Cannot be used in this stance/form
			if (GetErrorAtShapeshiftedCast(spellInfo, GetShapeshiftForm())
					== SPELL_CAST_OK)
				return; // and remove only not compatible at form change
		}

		if (item)
			RemoveAurasDueToItemSpell(item, spellInfo->Id); // un-apply all spells, not only at-equipped
		else
			RemoveAurasDueToSpell(spellInfo->Id); // un-apply spell (item set case)
	}
}

void Player::UpdateEquipSpellsAtFormChange() {
	for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i) {
		if (m_items[i] && !m_items[i]->IsBroken()
				&& CanUseAttackType(GetAttackBySlot(i))) {
			ApplyItemEquipSpell(m_items[i], false, true); // remove spells that not fit to form
			ApplyItemEquipSpell(m_items[i], true, true); // add spells that fit form but not active
		}
	}

	// item set bonuses not dependent from item broken state
	for (size_t setindex = 0; setindex < ItemSetEff.size(); ++setindex) {
		ItemSetEffect* eff = ItemSetEff[setindex];
		if (!eff)
			continue;

		for (uint32 y = 0; y < MAX_ITEM_SET_SPELLS; ++y) {
			SpellEntry const* spellInfo = eff->spells[y];
			if (!spellInfo)
				continue;

			ApplyEquipSpell(spellInfo, NULL, false, true); // remove spells that not fit to form
			ApplyEquipSpell(spellInfo, NULL, true, true); // add spells that fit form but not active
		}
	}
}
void Player::CastItemCombatSpell(Unit *target, WeaponAttackType attType,
		uint32 procVictim, uint32 procEx) {
	if (!target || !target->isAlive() || target == this)
		return;

	for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) {
		// If usable, try to cast item spell
		if (Item * item = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (!item->IsBroken() && CanUseAttackType(attType))
				if (ItemPrototype const *proto = item->GetProto()) {
					// Additional check for weapons
					if (proto->Class == ITEM_CLASS_WEAPON) {
						// offhand item cannot proc from main hand hit etc
						EquipmentSlots slot;
						switch (attType) {
						case BASE_ATTACK:
							slot = EQUIPMENT_SLOT_MAINHAND;
							break;
						case OFF_ATTACK:
							slot = EQUIPMENT_SLOT_OFFHAND;
							break;
						case RANGED_ATTACK:
							slot = EQUIPMENT_SLOT_RANGED;
							break;
						default:
							slot = EQUIPMENT_SLOT_END;
							break;
						}
						if (slot != i)
							continue;
						// Check if item is useable (forms or disarm)
						if (attType == BASE_ATTACK)
							if (!IsUseEquipedWeapon(true) && !IsInFeralForm())
								continue;
					}
					CastItemCombatSpell(target, attType, procVictim, procEx,
							item, proto);
				}
	}
}

void Player::CastItemCombatSpell(Unit *target, WeaponAttackType attType,
		uint32 procVictim, uint32 procEx, Item *item,
		ItemPrototype const * proto) {
	// Can do effect if any damage done to target
	if (procVictim & PROC_FLAG_TAKEN_DAMAGE)
	//if (damageInfo->procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE)
			{
		for (uint8 i = 0; i < MAX_ITEM_SPELLS; ++i) {
			_Spell const& spellData = proto->Spells[i];

			// no spell
			if (!spellData.SpellId)
				continue;

			// wrong triggering type
			if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_CHANCE_ON_HIT)
				continue;

			SpellEntry const *spellInfo = sSpellStore.LookupEntry(
					spellData.SpellId);
			if (!spellInfo) {
				sLog->outError("WORLD: unknown Item spellid %i",
						spellData.SpellId);
				continue;
			}

			// not allow proc extra attack spell at extra attack
			if (m_extraAttacks
					&& IsSpellHaveEffect(spellInfo,
							SPELL_EFFECT_ADD_EXTRA_ATTACKS))
				return;

			float chance = (float) spellInfo->procChance;

			if (spellData.SpellPPMRate) {
				if (spellData.SpellId == 52781) // Persuasive Strike
						{
					switch (target->GetEntry()) {
					default:
						return;
					case 28939:
					case 28940:
					case 28610:
						break;
					}
				}
				uint32 WeaponSpeed = GetAttackTime(attType);
				chance = GetPPMProcChance(WeaponSpeed, spellData.SpellPPMRate,
						spellInfo);
			} else if (chance > 100.0f) {
				chance = GetWeaponProcChance();
			}

			if (roll_chance_f(chance))
				CastSpell(target, spellInfo->Id, true, item);
		}
	}

	// item combat enchantments
	for (uint8 e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot) {
		uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(e_slot));
		SpellItemEnchantmentEntry const *pEnchant =
				sSpellItemEnchantmentStore.LookupEntry(enchant_id);
		if (!pEnchant)
			continue;
		for (uint8 s = 0; s < MAX_ITEM_ENCHANTMENT_EFFECTS; ++s) {
			if (pEnchant->type[s] != ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL)
				continue;

			SpellEnchantProcEntry const* entry =
					sSpellMgr->GetSpellEnchantProcEvent(enchant_id);

			if (entry && entry->procEx) {
				// Check hit/crit/dodge/parry requirement
				if ((entry->procEx & procEx) == 0)
					continue;
			} else {
				// Can do effect if any damage done to target
				if (!(procVictim & PROC_FLAG_TAKEN_DAMAGE))
					//if (!(damageInfo->procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE))
					continue;
			}

			SpellEntry const *spellInfo = sSpellStore.LookupEntry(
					pEnchant->spellid[s]);
			if (!spellInfo) {
				sLog->outError(
						"Player::CastItemCombatSpell(GUID: %u, name: %s, enchant: %i): unknown spell %i is casted, ignoring...",
						GetGUIDLow(), GetName(), pEnchant->ID,
						pEnchant->spellid[s]);
				continue;
			}

			float chance =
					pEnchant->amount[s] != 0 ?
							float(pEnchant->amount[s]) : GetWeaponProcChance();

			if (entry) {
				if (entry->PPMChance)
					chance = GetPPMProcChance(proto->Delay, entry->PPMChance,
							spellInfo);
				else if (entry->customChance)
					chance = (float) entry->customChance;
			}

			// Apply spell mods
			ApplySpellMod(pEnchant->spellid[s], SPELLMOD_CHANCE_OF_SUCCESS,
					chance);

			// Shiv has 100% chance to apply the poison
			if (FindCurrentSpellBySpellId(5938)
					&& e_slot == TEMP_ENCHANTMENT_SLOT)
				chance = 100.0f;

			if (roll_chance_f(chance)) {
				if (IsPositiveSpell(pEnchant->spellid[s]))
					CastSpell(this, pEnchant->spellid[s], true, item);
				else
					CastSpell(target, pEnchant->spellid[s], true, item);
			}
		}
	}
}

void Player::CastItemUseSpell(Item *item, SpellCastTargets const& targets,
		uint8 cast_count) {
	ItemPrototype const* proto = item->GetProto();
	// special learning case
	if (proto->Spells[0].SpellId == 483 || proto->Spells[0].SpellId == 55884) {
		uint32 learn_spell_id = proto->Spells[0].SpellId;
		uint32 learning_spell_id = proto->Spells[1].SpellId;

		SpellEntry const *spellInfo = sSpellStore.LookupEntry(learn_spell_id);
		if (!spellInfo) {
			sLog->outError(
					"Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring ",
					proto->ItemId, learn_spell_id);
			SendEquipError(EQUIP_ERR_NONE, item, NULL);
			return;
		}

		Spell *spell = new Spell(this, spellInfo, false);
		spell->m_CastItem = item;
		spell->m_cast_count = cast_count; //set count of casts
		spell->SetSpellValue(SPELLVALUE_BASE_POINT0, learning_spell_id);
		spell->prepare(&targets);
		return;
	}

	// use triggered flag only for items with many spell casts and for not first cast
	uint8 count = 0;

	// item spells casted at use
	for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) {
		_Spell const& spellData = proto->Spells[i];

		// no spell
		if (!spellData.SpellId)
			continue;

		// wrong triggering type
		if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
			continue;

		SpellEntry const *spellInfo = sSpellStore.LookupEntry(
				spellData.SpellId);
		if (!spellInfo) {
			sLog->outError(
					"Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring",
					proto->ItemId, spellData.SpellId);
			continue;
		}

		Spell *spell = new Spell(this, spellInfo, (count > 0));
		spell->m_CastItem = item;
		spell->m_cast_count = cast_count; // set count of casts
		spell->prepare(&targets);

		++count;
	}

	// Item enchantments spells casted at use
	for (uint8 e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot) {
		uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(e_slot));
		SpellItemEnchantmentEntry const *pEnchant =
				sSpellItemEnchantmentStore.LookupEntry(enchant_id);
		if (!pEnchant)
			continue;
		for (uint8 s = 0; s < MAX_ITEM_ENCHANTMENT_EFFECTS; ++s) {
			if (pEnchant->type[s] != ITEM_ENCHANTMENT_TYPE_USE_SPELL)
				continue;

			SpellEntry const *spellInfo = sSpellStore.LookupEntry(
					pEnchant->spellid[s]);
			if (!spellInfo) {
				sLog->outError(
						"Player::CastItemUseSpell Enchant %i, cast unknown spell %i",
						pEnchant->ID, pEnchant->spellid[s]);
				continue;
			}

			Spell *spell = new Spell(this, spellInfo, (count > 0));
			spell->m_CastItem = item;
			spell->m_cast_count = cast_count; // set count of casts
			spell->prepare(&targets);

			++count;
		}
	}
}

void Player::_RemoveAllItemMods() {
	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "_RemoveAllItemMods start.");

	for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i) {
		if (m_items[i]) {
			ItemPrototype const *proto = m_items[i]->GetProto();
			if (!proto)
				continue;

			// item set bonuses not dependent from item broken state
			if (proto->ItemSet)
				RemoveItemsSetItem(this, proto);

			if (m_items[i]->IsBroken() || !CanUseAttackType(GetAttackBySlot(i)))
				continue;

			ApplyItemEquipSpell(m_items[i], false);
			ApplyEnchantment(m_items[i], false);
		}
	}

	for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i) {
		if (m_items[i]) {
			if (m_items[i]->IsBroken() || !CanUseAttackType(GetAttackBySlot(i)))
				continue;
			ItemPrototype const *proto = m_items[i]->GetProto();
			if (!proto)
				continue;

			uint32 attacktype = Player::GetAttackBySlot(i);
			if (attacktype < MAX_ATTACK)
				_ApplyWeaponDependentAuraMods(m_items[i],
						WeaponAttackType(attacktype), false);

			_ApplyItemBonuses(proto, i, false);

			if (i == EQUIPMENT_SLOT_RANGED)
				_ApplyAmmoBonuses();
		}
	}

	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "_RemoveAllItemMods complete.");
}

void Player::_ApplyAllItemMods() {
	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "_ApplyAllItemMods start.");

	for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i) {
		if (m_items[i]) {
			if (m_items[i]->IsBroken() || !CanUseAttackType(GetAttackBySlot(i)))
				continue;

			ItemPrototype const *proto = m_items[i]->GetProto();
			if (!proto)
				continue;

			uint32 attacktype = Player::GetAttackBySlot(i);
			if (attacktype < MAX_ATTACK)
				_ApplyWeaponDependentAuraMods(m_items[i],
						WeaponAttackType(attacktype), true);

			_ApplyItemBonuses(proto, i, true);

			if (i == EQUIPMENT_SLOT_RANGED)
				_ApplyAmmoBonuses();
		}
	}

	for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i) {
		if (m_items[i]) {
			ItemPrototype const *proto = m_items[i]->GetProto();
			if (!proto)
				continue;

			// item set bonuses not dependent from item broken state
			if (proto->ItemSet)
				AddItemsSetItem(this, m_items[i]);

			if (m_items[i]->IsBroken() || !CanUseAttackType(GetAttackBySlot(i)))
				continue;

			ApplyItemEquipSpell(m_items[i], true);
			ApplyEnchantment(m_items[i], true);
		}
	}

	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "_ApplyAllItemMods complete.");
}

void Player::_ApplyAllLevelScaleItemMods(bool apply) {
	for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i) {
		if (m_items[i]) {
			if (m_items[i]->IsBroken() || !CanUseAttackType(GetAttackBySlot(i)))
				continue;

			ItemPrototype const *proto = m_items[i]->GetProto();
			if (!proto)
				continue;

			_ApplyItemBonuses(proto, i, apply, true);
		}
	}
}

void Player::_ApplyAmmoBonuses() {
	// check ammo
	//uint32 ammo_id = GetUInt32Value(PLAYER_AMMO_ID);
	//if (!ammo_id)
	//    return;

	//float currentAmmoDPS;

	//ItemPrototype const *ammo_proto = ObjectMgr::GetItemPrototype(ammo_id);
	//if (!ammo_proto || ammo_proto->Class != ITEM_CLASS_PROJECTILE || !CheckAmmoCompatibility(ammo_proto))
	//    currentAmmoDPS = 0.0f;
	//else
	//    currentAmmoDPS = (ammo_proto->Damage[0].DamageMin + ammo_proto->Damage[0].DamageMax) / 2;

	//if (currentAmmoDPS == GetAmmoDPS())
	//    return;

	//m_ammoDPS = currentAmmoDPS;

	//if (CanModifyStats())
	//    UpdateDamagePhysical(RANGED_ATTACK);
}

bool Player::CheckAmmoCompatibility(const ItemPrototype *ammo_proto) const {
	/*
	 if (!ammo_proto)
	 return false;

	 // check ranged weapon
	 Item *weapon = GetWeaponForAttack(RANGED_ATTACK);
	 if (!weapon  || weapon->IsBroken())
	 return false;

	 ItemPrototype const* weapon_proto = weapon->GetProto();
	 if (!weapon_proto || weapon_proto->Class != ITEM_CLASS_WEAPON)
	 return false;

	 // check ammo ws. weapon compatibility
	 switch (weapon_proto->SubClass)
	 {
	 case ITEM_SUBCLASS_WEAPON_BOW:
	 case ITEM_SUBCLASS_WEAPON_CROSSBOW:
	 if (ammo_proto->SubClass != ITEM_SUBCLASS_ARROW)
	 return false;
	 break;
	 case ITEM_SUBCLASS_WEAPON_GUN:
	 if (ammo_proto->SubClass != ITEM_SUBCLASS_BULLET)
	 return false;
	 break;
	 default:
	 return false;
	 }*/

	return true;
}

/*  If in a battleground a player dies, and an enemy removes the insignia, the player's bones is lootable
 Called by remove insignia spell effect    */
void Player::RemovedInsignia(Player* looterPlr) {
	if (!GetBattlegroundId())
		return;

	// If not released spirit, do it !
	if (m_deathTimer > 0) {
		m_deathTimer = 0;
		BuildPlayerRepop();
		RepopAtGraveyard();
	}

	Corpse *corpse = GetCorpse();
	if (!corpse)
		return;

	// We have to convert player corpse to bones, not to be able to resurrect there
	// SpawnCorpseBones isn't handy, 'cos it saves player while he in BG
	Corpse *bones = sObjectAccessor->ConvertCorpseForPlayer(GetGUID(), true);
	if (!bones)
		return;

	// Now we must make bones lootable, and send player loot
	bones->SetFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE);

	// We store the level of our player in the gold field
	// We retrieve this information at Player::SendLoot()
	bones->loot.gold = getLevel();
	bones->lootRecipient = looterPlr;
	looterPlr->SendLoot(bones->GetGUID(), LOOT_INSIGNIA);
}

void Player::SendLootRelease(uint64 guid) {
	WorldPacket data(SMSG_LOOT_RELEASE_RESPONSE, (9));
	data << uint64(guid) << uint8(1);
	SendDirectMessage(&data);
}

void Player::SendLoot(uint64 guid, LootType loot_type) {
	if (uint64 lguid = GetLootGUID())
		m_session->DoLootRelease(lguid);

	Loot *loot = 0;
	PermissionTypes permission = ALL_PERMISSION;

	sLog->outDebug(LOG_FILTER_PLAYER_LOADING, "Player::SendLoot");
	if (IS_GAMEOBJECT_GUID(guid)) {
		sLog->outDebug(LOG_FILTER_LOOT, "IS_GAMEOBJECT_GUID(guid)");
		GameObject *go = GetMap()->GetGameObject(guid);

		// not check distance for GO in case owned GO (fishing bobber case, for example)
		// And permit out of range GO with no owner in case fishing hole
		if (!go
				|| (loot_type != LOOT_FISHINGHOLE
						&& (loot_type != LOOT_FISHING
								|| go->GetOwnerGUID() != GetGUID())
						&& !go->IsWithinDistInMap(this, INTERACTION_DISTANCE))
				|| (loot_type == LOOT_CORPSE && go->GetRespawnTime()
						&& go->isSpawnedByDefault())) {
			SendLootRelease(guid);
			return;
		}

		loot = &go->loot;

		if (go->getLootState() == GO_READY) {
			uint32 lootid = go->GetGOInfo()->GetLootId();

			//TODO: fix this big hack
			if ((go->GetEntry() == BG_AV_OBJECTID_MINE_N
					|| go->GetEntry() == BG_AV_OBJECTID_MINE_S))
				if (Battleground *bg = GetBattleground())
					if (bg->GetTypeID(true) == BATTLEGROUND_AV)
						if (!(((BattlegroundAV*) bg)->PlayerCanDoMineQuest(
								go->GetEntry(), GetTeam()))) {
							SendLootRelease(guid);
							return;
						}

			if (lootid) {
				loot->clear();

				Group* group = GetGroup();
				bool groupRules = (group
						&& go->GetGOInfo()->type == GAMEOBJECT_TYPE_CHEST
						&& go->GetGOInfo()->chest.groupLootRules);

				// check current RR player and get next if necessary
				if (groupRules)
					group->UpdateLooterGuid(go, true);

				loot->FillLoot(lootid, LootTemplates_Gameobject, this,
						!groupRules, false, go->GetLootMode());

				// get next RR player (for next loot)
				if (groupRules)
					group->UpdateLooterGuid(go);
			}

			if (loot_type == LOOT_FISHING)
				go->getFishLoot(loot, this);

			if (go->GetGOInfo()->type == GAMEOBJECT_TYPE_CHEST
					&& go->GetGOInfo()->chest.groupLootRules) {
				if (Group* group = GetGroup()) {
					switch (group->GetLootMethod()) {
					case GROUP_LOOT:
						// GroupLoot: rolls items over threshold. Items with quality < threshold, round robin
						group->GroupLoot(loot, go);
						break;
					case NEED_BEFORE_GREED:
						group->NeedBeforeGreed(loot, go);
						break;
					case MASTER_LOOT:
						group->MasterLoot(loot, go);
						break;
					default:
						break;
					}
				}
			}

			go->SetLootState(GO_ACTIVATED);
		}

		if (go->getLootState() == GO_ACTIVATED) {
			if (Group* group = GetGroup()) {
				switch (group->GetLootMethod()) {
				case MASTER_LOOT:
					permission = MASTER_PERMISSION;
					break;
				case FREE_FOR_ALL:
					permission = ALL_PERMISSION;
					break;
				case ROUND_ROBIN:
					permission = ROUND_ROBIN_PERMISSION;
					break;
				default:
					permission = GROUP_PERMISSION;
					break;
				}
			} else
				permission = ALL_PERMISSION;
		}
	} else if (IS_ITEM_GUID(guid)) {
		Item *item = GetItemByGuid(guid);

		if (!item) {
			SendLootRelease(guid);
			return;
		}

		permission = OWNER_PERMISSION;

		loot = &item->loot;

		if (!item->m_lootGenerated) {
			item->m_lootGenerated = true;
			loot->clear();

			switch (loot_type) {
			case LOOT_DISENCHANTING:
				loot->FillLoot(item->GetProto()->DisenchantID,
						LootTemplates_Disenchant, this, true);
				break;
			case LOOT_PROSPECTING:
				loot->FillLoot(item->GetEntry(), LootTemplates_Prospecting,
						this, true);
				break;
			case LOOT_MILLING:
				loot->FillLoot(item->GetEntry(), LootTemplates_Milling, this,
						true);
				break;
			default:
				loot->FillLoot(item->GetEntry(), LootTemplates_Item, this,
						true);
				loot->generateMoneyLoot(item->GetProto()->MinMoneyLoot,
						item->GetProto()->MaxMoneyLoot);
				break;
			}
		}
	} else if (IS_CORPSE_GUID(guid)) // remove insignia
	{
		Corpse *bones = ObjectAccessor::GetCorpse(*this, guid);

		if (!bones || !(loot_type == LOOT_CORPSE || loot_type == LOOT_INSIGNIA)
				|| bones->GetType() != CORPSE_BONES) {
			SendLootRelease(guid);
			return;
		}

		loot = &bones->loot;

		if (!bones->lootForBody) {
			bones->lootForBody = true;
			uint32 pLevel = bones->loot.gold;
			bones->loot.clear();
			if (Battleground *bg = GetBattleground())
				if (bg->GetTypeID(true) == BATTLEGROUND_AV)
					loot->FillLoot(1, LootTemplates_Creature, this, true);
			// It may need a better formula
			// Now it works like this: lvl10: ~6copper, lvl70: ~9silver
			bones->loot.gold = uint32(
					urand(50, 150) * 0.016f * pow(float(pLevel) / 5.76f, 2.5f)
							* sWorld->getRate(RATE_DROP_MONEY));
		}

		if (bones->lootRecipient != this)
			permission = NONE_PERMISSION;
		else
			permission = OWNER_PERMISSION;
	} else {
		Creature *creature = GetMap()->GetCreature(guid);

		// must be in range and creature must be alive for pickpocket and must be dead for another loot
		if (!creature
				|| creature->isAlive() != (loot_type == LOOT_PICKPOCKETING)
				|| !creature->IsWithinDistInMap(this, INTERACTION_DISTANCE)) {
			SendLootRelease(guid);
			return;
		}

		if (loot_type == LOOT_PICKPOCKETING && IsFriendlyTo(creature)) {
			SendLootRelease(guid);
			return;
		}

		loot = &creature->loot;

		if (loot_type == LOOT_PICKPOCKETING) {
			if (!creature->lootForPickPocketed) {
				creature->lootForPickPocketed = true;
				loot->clear();

				if (uint32 lootid = creature->GetCreatureInfo()->pickpocketLootId)
					loot->FillLoot(lootid, LootTemplates_Pickpocketing, this,
							true);

				// Generate extra money for pick pocket loot
				const uint32 a = urand(0, creature->getLevel() / 2);
				const uint32 b = urand(0, getLevel() / 2);
				loot->gold = uint32(
						10 * (a + b) * sWorld->getRate(RATE_DROP_MONEY));
				permission = OWNER_PERMISSION;
			}
		} else {
			// the player whose group may loot the corpse
			Player *recipient = creature->GetLootRecipient();
			if (!recipient)
				return;

			if (!creature->lootForBody) {
				creature->lootForBody = true;

				// for creature, loot is filled when creature is killed.

				if (Group* group = recipient->GetGroup()) {
					switch (group->GetLootMethod()) {
					case GROUP_LOOT:
						// GroupLoot: rolls items over threshold. Items with quality < threshold, round robin
						group->GroupLoot(loot, creature);
						break;
					case NEED_BEFORE_GREED:
						group->NeedBeforeGreed(loot, creature);
						break;
					case MASTER_LOOT:
						group->MasterLoot(loot, creature);
						break;
					default:
						break;
					}
				}
			}

			// possible only if creature->lootForBody && loot->empty() at spell cast check
			if (loot_type == LOOT_SKINNING) {
				loot->clear();
				loot->FillLoot(creature->GetCreatureInfo()->SkinLootId,
						LootTemplates_Skinning, this, true);
				permission = OWNER_PERMISSION;
			}
			// set group rights only for loot_type != LOOT_SKINNING
			else {
				if (Group* group = GetGroup()) {
					if (group == recipient->GetGroup()) {
						switch (group->GetLootMethod()) {
						case MASTER_LOOT:
							permission = MASTER_PERMISSION;
							break;
						case FREE_FOR_ALL:
							permission = ALL_PERMISSION;
							break;
						case ROUND_ROBIN:
							permission = ROUND_ROBIN_PERMISSION;
							break;
						default:
							permission = GROUP_PERMISSION;
							break;
						}
					} else
						permission = NONE_PERMISSION;
				} else if (recipient == this)
					permission = OWNER_PERMISSION;
				else
					permission = NONE_PERMISSION;
			}
		}
	}

	SetLootGUID(guid);

	// LOOT_INSIGNIA and LOOT_FISHINGHOLE unsupported by client
	switch (loot_type) {
	case LOOT_INSIGNIA:
		loot_type = LOOT_SKINNING;
		break;
	case LOOT_FISHINGHOLE:
		loot_type = LOOT_FISHING;
		break;
	default:
		break;
	}

	// need know merged fishing/corpse loot type for achievements
	loot->loot_type = loot_type;

	WorldPacket data(SMSG_LOOT_RESPONSE, (9 + 50 + 2), true);
	data << uint64(guid);
	data << uint8(loot_type);
	data << LootView(*loot, this, permission);

	SendDirectMessage(&data);

	// add 'this' player as one of the players that are looting 'loot'
	if (permission != NONE_PERMISSION)
		loot->AddLooter(GetGUID());

	if (loot_type == LOOT_CORPSE && !IS_ITEM_GUID(guid))
		SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING);
}

void Player::SendNotifyLootMoneyRemoved() {
	WorldPacket data(SMSG_LOOT_CLEAR_MONEY, 0);
	GetSession()->SendPacket(&data);
}

void Player::SendNotifyLootItemRemoved(uint8 lootSlot) {
	WorldPacket data(SMSG_LOOT_REMOVED, 3, true);
	data << uint8(lootSlot);
	GetSession()->SendPacket(&data);
}

void Player::SendUpdateWorldState(uint32 Field, uint32 Value) {
	WorldPacket data(SMSG_UPDATE_WORLD_STATE, 8);
	data << Field;
	data << Value;
	GetSession()->SendPacket(&data);
}

void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) {
	// data depends on zoneid/mapid...
	Battleground* bg = GetBattleground();
	uint16 NumberOfFields = 0;
	uint32 mapid = GetMapId();
	OutdoorPvP * pvp = sOutdoorPvPMgr->GetOutdoorPvPToZoneId(zoneid);

	sLog->outDebug(LOG_FILTER_NETWORKIO,
			"Sending SMSG_INIT_WORLD_STATES to Map: %u, Zone: %u", mapid,
			zoneid);

	// may be exist better way to do this...
	switch (zoneid) {
	case 0:
	case 1:
	case 4:
	case 8:
	case 10:
	case 11:
	case 12:
	case 36:
	case 38:
	case 40:
	case 41:
	case 51:
	case 267:
	case 1519:
	case 1537:
	case 2257:
	case 2918:
		NumberOfFields = 8;
		break;
	case 139:
		NumberOfFields = 41;
		break;
	case 1377:
		NumberOfFields = 15;
		break;
	case 2597:
		NumberOfFields = 83;
		break;
	case 3277:
		NumberOfFields = 16;
		break;
	case 3358:
	case 3820:
		NumberOfFields = 40;
		break;
	case 3483:
		NumberOfFields = 27;
		break;
	case 3518:
		NumberOfFields = 39;
		break;
	case 3519:
		NumberOfFields = 38;
		break;
	case 3521:
		NumberOfFields = 37;
		break;
	case 3698:
	case 3702:
	case 3968:
		NumberOfFields = 11;
		break;
	case 4378:
		NumberOfFields = 11;
		break;
	case 3703:
		NumberOfFields = 11;
		break;
	case 4384:
		NumberOfFields = 30;
		break;
	case 4710:
		NumberOfFields = 28;
		break;
	case 5449:
		NumberOfFields = 92;
		break;
	default:
		NumberOfFields = 12;
		break;
	}

	//Need to Rewrote This Packet after we get sniffer
	WorldPacket data(SMSG_INIT_WORLD_STATES,
			(4 + 4 + 4 + 2 + (NumberOfFields * 8)));
	data << uint32(mapid); // mapid
	data << uint32(zoneid); // zone id
	data << uint32(areaid); // area id, new 2.1.0
	data << uint16(NumberOfFields); // count of uint64 blocks
	data << uint32(5334) << uint32(0); // Unknown
	data << uint32(5332) << uint32(0); // Unknown
	data << uint32(5333) << uint32(0); // 1 - TB "In Progress"

	data << uint32(3191)
			<< uint32(sWorld->getBoolConfig(CONFIG_ARENA_SEASON_IN_PROGRESS));
	data << uint32(3901)
			<< uint32(sWorld->getIntConfig(CONFIG_ARENA_SEASON_ID));

    // May be send timer to start Wintergrasp
    if(sWorld->GetWintergrapsState()==4354)
        data << uint32(0x1102) << sWorld->GetWintergrapsTimer();
    else
        data << uint32(0xEC5) << sWorld->GetWintergrapsTimer();
    // ---
			
	if (mapid == 530) // Outland
			{
		data << uint32(0x9bf) << uint32(0x0); // 7
		data << uint32(0x9bd) << uint32(0xF); // 8
		data << uint32(0x9bb) << uint32(0xF); // 9
	}

	// insert <field> <value>
	switch (zoneid) {
	case 1: // Dun Morogh - Unknown Value maybe some day we will know that
		data << uint32(3094) << uint32(0x1);
		data << uint32(3096) << uint32(0x0);
		break;
	case 11: // Wetlands
	case 12: // Elwynn Forest
	case 38: // Loch Modan
	case 40: // Westfall
	case 51: // Searing Gorge
	case 1519: // Stormwind City
	case 1537: // Ironforge
	case 2257: // Deeprun Tram
		break;
	case 139: // Eastern Plaguelands
		if (pvp && pvp->GetTypeId() == OUTDOOR_PVP_EP)
			pvp->FillInitialWorldStates(data);
		else {
			data << uint32(0x97a) << uint32(0x0); // 10 2426
			data << uint32(0x917) << uint32(0x0); // 11 2327
			data << uint32(0x918) << uint32(0x0); // 12 2328
			data << uint32(0x97b) << uint32(0x32); // 13 2427
			data << uint32(0x97c) << uint32(0x32); // 14 2428
			data << uint32(0x933) << uint32(0x1); // 15 2355
			data << uint32(0x946) << uint32(0x0); // 16 2374
			data << uint32(0x947) << uint32(0x0); // 17 2375
			data << uint32(0x948) << uint32(0x0); // 18 2376
			data << uint32(0x949) << uint32(0x0); // 19 2377
			data << uint32(0x94a) << uint32(0x0); // 20 2378
			data << uint32(0x94b) << uint32(0x0); // 21 2379
			data << uint32(0x932) << uint32(0x0); // 22 2354
			data << uint32(0x934) << uint32(0x0); // 23 2356
			data << uint32(0x935) << uint32(0x0); // 24 2357
			data << uint32(0x936) << uint32(0x0); // 25 2358
			data << uint32(0x937) << uint32(0x0); // 26 2359
			data << uint32(0x938) << uint32(0x0); // 27 2360
			data << uint32(0x939) << uint32(0x1); // 28 2361
			data << uint32(0x930) << uint32(0x1); // 29 2352
			data << uint32(0x93a) << uint32(0x0); // 30 2362
			data << uint32(0x93b) << uint32(0x0); // 31 2363
			data << uint32(0x93c) << uint32(0x0); // 32 2364
			data << uint32(0x93d) << uint32(0x0); // 33 2365
			data << uint32(0x944) << uint32(0x0); // 34 2372
			data << uint32(0x945) << uint32(0x0); // 35 2373
			data << uint32(0x931) << uint32(0x1); // 36 2353
			data << uint32(0x93e) << uint32(0x0); // 37 2366
			data << uint32(0x931) << uint32(0x1); // 38 2367 ??  grey horde not in dbc! send for consistency's sake, and to match field count
			data << uint32(0x940) << uint32(0x0); // 39 2368
			data << uint32(0x941) << uint32(0x0); // 7 2369
			data << uint32(0x942) << uint32(0x0); // 8 2370
			data << uint32(0x943) << uint32(0x0); // 9 2371
		}
		break;
	case 1377: // Silithus
		if (pvp && pvp->GetTypeId() == OUTDOOR_PVP_SI)
			pvp->FillInitialWorldStates(data);
		else {
			// states are always shown
			data << uint32(2313) << uint32(0x0); // 7 ally silityst gathered
			data << uint32(2314) << uint32(0x0); // 8 horde silityst gathered
			data << uint32(2317) << uint32(0x0); // 9 max silithyst
		}
		// dunno about these... aq opening event maybe?
		data << uint32(2322) << uint32(0x0); // 10 sandworm N
		data << uint32(2323) << uint32(0x0); // 11 sandworm S
		data << uint32(2324) << uint32(0x0); // 12 sandworm SW
		data << uint32(2325) << uint32(0x0); // 13 sandworm E
		break;
	case 2597: // Alterac Valley
		if (bg && bg->GetTypeID(true) == BATTLEGROUND_AV)
			bg->FillInitialWorldStates(data);
		else {
			data << uint32(0x7ae) << uint32(0x1); // 7 snowfall n
			data << uint32(0x532) << uint32(0x1); // 8 frostwolfhut hc
			data << uint32(0x531) << uint32(0x0); // 9 frostwolfhut ac
			data << uint32(0x52e) << uint32(0x0); // 10 stormpike firstaid a_a
			data << uint32(0x571) << uint32(0x0); // 11 east frostwolf tower horde assaulted -unused
			data << uint32(0x570) << uint32(0x0); // 12 west frostwolf tower horde assaulted - unused
			data << uint32(0x567) << uint32(0x1); // 13 frostwolfe c
			data << uint32(0x566) << uint32(0x1); // 14 frostwolfw c
			data << uint32(0x550) << uint32(0x1); // 15 irondeep (N) ally
			data << uint32(0x544) << uint32(0x0); // 16 ice grave a_a
			data << uint32(0x536) << uint32(0x0); // 17 stormpike grave h_c
			data << uint32(0x535) << uint32(0x1); // 18 stormpike grave a_c
			data << uint32(0x518) << uint32(0x0); // 19 stoneheart grave a_a
			data << uint32(0x517) << uint32(0x0); // 20 stoneheart grave h_a
			data << uint32(0x574) << uint32(0x0); // 21 1396 unk
			data << uint32(0x573) << uint32(0x0); // 22 iceblood tower horde assaulted -unused
			data << uint32(0x572) << uint32(0x0); // 23 towerpoint horde assaulted - unused
			data << uint32(0x56f) << uint32(0x0); // 24 1391 unk
			data << uint32(0x56e) << uint32(0x0); // 25 iceblood a
			data << uint32(0x56d) << uint32(0x0); // 26 towerp a
			data << uint32(0x56c) << uint32(0x0); // 27 frostwolfe a
			data << uint32(0x56b) << uint32(0x0); // 28 froswolfw a
			data << uint32(0x56a) << uint32(0x1); // 29 1386 unk
			data << uint32(0x569) << uint32(0x1); // 30 iceblood c
			data << uint32(0x568) << uint32(0x1); // 31 towerp c
			data << uint32(0x565) << uint32(0x0); // 32 stoneh tower a
			data << uint32(0x564) << uint32(0x0); // 33 icewing tower a
			data << uint32(0x563) << uint32(0x0); // 34 dunn a
			data << uint32(0x562) << uint32(0x0); // 35 duns a
			data << uint32(0x561) << uint32(0x0); // 36 stoneheart bunker alliance assaulted - unused
			data << uint32(0x560) << uint32(0x0); // 37 icewing bunker alliance assaulted - unused
			data << uint32(0x55f) << uint32(0x0); // 38 dunbaldar south alliance assaulted - unused
			data << uint32(0x55e) << uint32(0x0); // 39 dunbaldar north alliance assaulted - unused
			data << uint32(0x55d) << uint32(0x0); // 40 stone tower d
			data << uint32(0x3c6) << uint32(0x0); // 41 966 unk
			data << uint32(0x3c4) << uint32(0x0); // 42 964 unk
			data << uint32(0x3c2) << uint32(0x0); // 43 962 unk
			data << uint32(0x516) << uint32(0x1); // 44 stoneheart grave a_c
			data << uint32(0x515) << uint32(0x0); // 45 stonheart grave h_c
			data << uint32(0x3b6) << uint32(0x0); // 46 950 unk
			data << uint32(0x55c) << uint32(0x0); // 47 icewing tower d
			data << uint32(0x55b) << uint32(0x0); // 48 dunn d
			data << uint32(0x55a) << uint32(0x0); // 49 duns d
			data << uint32(0x559) << uint32(0x0); // 50 1369 unk
			data << uint32(0x558) << uint32(0x0); // 51 iceblood d
			data << uint32(0x557) << uint32(0x0); // 52 towerp d
			data << uint32(0x556) << uint32(0x0); // 53 frostwolfe d
			data << uint32(0x555) << uint32(0x0); // 54 frostwolfw d
			data << uint32(0x554) << uint32(0x1); // 55 stoneh tower c
			data << uint32(0x553) << uint32(0x1); // 56 icewing tower c
			data << uint32(0x552) << uint32(0x1); // 57 dunn c
			data << uint32(0x551) << uint32(0x1); // 58 duns c
			data << uint32(0x54f) << uint32(0x0); // 59 irondeep (N) horde
			data << uint32(0x54e) << uint32(0x0); // 60 irondeep (N) ally
			data << uint32(0x54d) << uint32(0x1); // 61 mine (S) neutral
			data << uint32(0x54c) << uint32(0x0); // 62 mine (S) horde
			data << uint32(0x54b) << uint32(0x0); // 63 mine (S) ally
			data << uint32(0x545) << uint32(0x0); // 64 iceblood h_a
			data << uint32(0x543) << uint32(0x1); // 65 iceblod h_c
			data << uint32(0x542) << uint32(0x0); // 66 iceblood a_c
			data << uint32(0x540) << uint32(0x0); // 67 snowfall h_a
			data << uint32(0x53f) << uint32(0x0); // 68 snowfall a_a
			data << uint32(0x53e) << uint32(0x0); // 69 snowfall h_c
			data << uint32(0x53d) << uint32(0x0); // 70 snowfall a_c
			data << uint32(0x53c) << uint32(0x0); // 71 frostwolf g h_a
			data << uint32(0x53b) << uint32(0x0); // 72 frostwolf g a_a
			data << uint32(0x53a) << uint32(0x1); // 73 frostwolf g h_c
			data << uint32(0x539) << uint32(0x0); // 74 frostwolf g a_c
			data << uint32(0x538) << uint32(0x0); // 75 stormpike grave h_a
			data << uint32(0x537) << uint32(0x0); // 76 stormpike grave a_a
			data << uint32(0x534) << uint32(0x0); // 77 frostwolf hut h_a
			data << uint32(0x533) << uint32(0x0); // 78 frostwolf hut a_a
			data << uint32(0x530) << uint32(0x0); // 79 stormpike first aid h_a
			data << uint32(0x52f) << uint32(0x0); // 80 stormpike first aid h_c
			data << uint32(0x52d) << uint32(0x1); // 81 stormpike first aid a_c
		}
		break;
	case 3277: // Warsong Gulch
		if (bg && bg->GetTypeID(true) == BATTLEGROUND_WS)
			bg->FillInitialWorldStates(data);
		else {
			data << uint32(0x62d) << uint32(0x0); // 7 1581 alliance flag captures
			data << uint32(0x62e) << uint32(0x0); // 8 1582 horde flag captures
			data << uint32(0x609) << uint32(0x0); // 9 1545 unk, set to 1 on alliance flag pickup...
			data << uint32(0x60a) << uint32(0x0); // 10 1546 unk, set to 1 on horde flag pickup, after drop it's -1
			data << uint32(0x60b) << uint32(0x2); // 11 1547 unk
			data << uint32(0x641) << uint32(0x3); // 12 1601 unk (max flag captures?)
			data << uint32(0x922) << uint32(0x1); // 13 2338 horde (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
			data << uint32(0x923) << uint32(0x1); // 14 2339 alliance (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
		}
		break;
	case 3358: // Arathi Basin
		if (bg && bg->GetTypeID(true) == BATTLEGROUND_AB)
			bg->FillInitialWorldStates(data);
		else {
			data << uint32(0x6e7) << uint32(0x0); // 7 1767 stables alliance
			data << uint32(0x6e8) << uint32(0x0); // 8 1768 stables horde
			data << uint32(0x6e9) << uint32(0x0); // 9 1769 unk, ST?
			data << uint32(0x6ea) << uint32(0x0); // 10 1770 stables (show/hide)
			data << uint32(0x6ec) << uint32(0x0); // 11 1772 farm (0 - horde controlled, 1 - alliance controlled)
			data << uint32(0x6ed) << uint32(0x0); // 12 1773 farm (show/hide)
			data << uint32(0x6ee) << uint32(0x0); // 13 1774 farm color
			data << uint32(0x6ef) << uint32(0x0); // 14 1775 gold mine color, may be FM?
			data << uint32(0x6f0) << uint32(0x0); // 15 1776 alliance resources
			data << uint32(0x6f1) << uint32(0x0); // 16 1777 horde resources
			data << uint32(0x6f2) << uint32(0x0); // 17 1778 horde bases
			data << uint32(0x6f3) << uint32(0x0); // 18 1779 alliance bases
			data << uint32(0x6f4) << uint32(0x7d0); // 19 1780 max resources (2000)
			data << uint32(0x6f6) << uint32(0x0); // 20 1782 blacksmith color
			data << uint32(0x6f7) << uint32(0x0); // 21 1783 blacksmith (show/hide)
			data << uint32(0x6f8) << uint32(0x0); // 22 1784 unk, bs?
			data << uint32(0x6f9) << uint32(0x0); // 23 1785 unk, bs?
			data << uint32(0x6fb) << uint32(0x0); // 24 1787 gold mine (0 - horde contr, 1 - alliance contr)
			data << uint32(0x6fc) << uint32(0x0); // 25 1788 gold mine (0 - conflict, 1 - horde)
			data << uint32(0x6fd) << uint32(0x0); // 26 1789 gold mine (1 - show/0 - hide)
			data << uint32(0x6fe) << uint32(0x0); // 27 1790 gold mine color
			data << uint32(0x700) << uint32(0x0); // 28 1792 gold mine color, wtf?, may be LM?
			data << uint32(0x701) << uint32(0x0); // 29 1793 lumber mill color (0 - conflict, 1 - horde contr)
			data << uint32(0x702) << uint32(0x0); // 30 1794 lumber mill (show/hide)
			data << uint32(0x703) << uint32(0x0); // 31 1795 lumber mill color color
			data << uint32(0x732) << uint32(0x1); // 32 1842 stables (1 - uncontrolled)
			data << uint32(0x733) << uint32(0x1); // 33 1843 gold mine (1 - uncontrolled)
			data << uint32(0x734) << uint32(0x1); // 34 1844 lumber mill (1 - uncontrolled)
			data << uint32(0x735) << uint32(0x1); // 35 1845 farm (1 - uncontrolled)
			data << uint32(0x736) << uint32(0x1); // 36 1846 blacksmith (1 - uncontrolled)
			data << uint32(0x745) << uint32(0x2); // 37 1861 unk
			data << uint32(0x7a3) << uint32(0x708); // 38 1955 warning limit (1800)
		}
		break;
	case 3820: // Eye of the Storm
		if (bg && bg->GetTypeID(true) == BATTLEGROUND_EY)
			bg->FillInitialWorldStates(data);
		else {
			data << uint32(0xac1) << uint32(0x0); // 7  2753 Horde Bases
			data << uint32(0xac0) << uint32(0x0); // 8  2752 Alliance Bases
			data << uint32(0xab6) << uint32(0x0); // 9  2742 Mage Tower - Horde conflict
			data << uint32(0xab5) << uint32(0x0); // 10 2741 Mage Tower - Alliance conflict
			data << uint32(0xab4) << uint32(0x0); // 11 2740 Fel Reaver - Horde conflict
			data << uint32(0xab3) << uint32(0x0); // 12 2739 Fel Reaver - Alliance conflict
			data << uint32(0xab2) << uint32(0x0); // 13 2738 Draenei - Alliance conflict
			data << uint32(0xab1) << uint32(0x0); // 14 2737 Draenei - Horde conflict
			data << uint32(0xab0) << uint32(0x0); // 15 2736 unk // 0 at start
			data << uint32(0xaaf) << uint32(0x0); // 16 2735 unk // 0 at start
			data << uint32(0xaad) << uint32(0x0); // 17 2733 Draenei - Horde control
			data << uint32(0xaac) << uint32(0x0); // 18 2732 Draenei - Alliance control
			data << uint32(0xaab) << uint32(0x1); // 19 2731 Draenei uncontrolled (1 - yes, 0 - no)
			data << uint32(0xaaa) << uint32(0x0); // 20 2730 Mage Tower - Alliance control
			data << uint32(0xaa9) << uint32(0x0); // 21 2729 Mage Tower - Horde control
			data << uint32(0xaa8) << uint32(0x1); // 22 2728 Mage Tower uncontrolled (1 - yes, 0 - no)
			data << uint32(0xaa7) << uint32(0x0); // 23 2727 Fel Reaver - Horde control
			data << uint32(0xaa6) << uint32(0x0); // 24 2726 Fel Reaver - Alliance control
			data << uint32(0xaa5) << uint32(0x1); // 25 2725 Fel Reaver uncontrolled (1 - yes, 0 - no)
			data << uint32(0xaa4) << uint32(0x0); // 26 2724 Boold Elf - Horde control
			data << uint32(0xaa3) << uint32(0x0); // 27 2723 Boold Elf - Alliance control
			data << uint32(0xaa2) << uint32(0x1); // 28 2722 Boold Elf uncontrolled (1 - yes, 0 - no)
			data << uint32(0xac5) << uint32(0x1); // 29 2757 Flag (1 - show, 0 - hide) - doesn't work exactly this way!
			data << uint32(0xad2) << uint32(0x1); // 30 2770 Horde top-stats (1 - show, 0 - hide) // 02 -> horde picked up the flag
			data << uint32(0xad1) << uint32(0x1); // 31 2769 Alliance top-stats (1 - show, 0 - hide) // 02 -> alliance picked up the flag
			data << uint32(0xabe) << uint32(0x0); // 32 2750 Horde resources
			data << uint32(0xabd) << uint32(0x0); // 33 2749 Alliance resources
			data << uint32(0xa05) << uint32(0x8e); // 34 2565 unk, constant?
			data << uint32(0xaa0) << uint32(0x0); // 35 2720 Capturing progress-bar (100 -> empty (only grey), 0 -> blue|red (no grey), default 0)
			data << uint32(0xa9f) << uint32(0x0); // 36 2719 Capturing progress-bar (0 - left, 100 - right)
			data << uint32(0xa9e) << uint32(0x0); // 37 2718 Capturing progress-bar (1 - show, 0 - hide)
			data << uint32(0xc0d) << uint32(0x17b); // 38 3085 unk
			// and some more ... unknown
		}
		break;
		// any of these needs change! the client remembers the prev setting!
		// ON EVERY ZONE LEAVE, RESET THE OLD ZONE'S WORLD STATE, BUT AT LEAST THE UI STUFF!
	case 3483: // Hellfire Peninsula
		if (pvp && pvp->GetTypeId() == OUTDOOR_PVP_HP)
			pvp->FillInitialWorldStates(data);
		else {
			data << uint32(0x9ba) << uint32(0x1); // 10 // add ally tower main gui icon       // maybe should be sent only on login?
			data << uint32(0x9b9) << uint32(0x1); // 11 // add horde tower main gui icon      // maybe should be sent only on login?
			data << uint32(0x9b5) << uint32(0x0); // 12 // show neutral broken hill icon      // 2485
			data << uint32(0x9b4) << uint32(0x1); // 13 // show icon above broken hill        // 2484
			data << uint32(0x9b3) << uint32(0x0); // 14 // show ally broken hill icon         // 2483
			data << uint32(0x9b2) << uint32(0x0); // 15 // show neutral overlook icon         // 2482
			data << uint32(0x9b1) << uint32(0x1); // 16 // show the overlook arrow            // 2481
			data << uint32(0x9b0) << uint32(0x0); // 17 // show ally overlook icon            // 2480
			data << uint32(0x9ae) << uint32(0x0); // 18 // horde pvp objectives captured      // 2478
			data << uint32(0x9ac) << uint32(0x0); // 19 // ally pvp objectives captured       // 2476
			data << uint32(2475) << uint32(100); //: ally / horde slider grey area                              // show only in direct vicinity!
			data << uint32(2474) << uint32(50); //: ally / horde slider percentage, 100 for ally, 0 for horde  // show only in direct vicinity!
			data << uint32(2473) << uint32(0); //: ally / horde slider display                                // show only in direct vicinity!
			data << uint32(0x9a8) << uint32(0x0); // 20 // show the neutral stadium icon      // 2472
			data << uint32(0x9a7) << uint32(0x0); // 21 // show the ally stadium icon         // 2471
			data << uint32(0x9a6) << uint32(0x1); // 22 // show the horde stadium icon        // 2470
		}
		break;
	case 3518: // Nagrand
		if (pvp && pvp->GetTypeId() == OUTDOOR_PVP_NA)
			pvp->FillInitialWorldStates(data);
		else {
			data << uint32(2503) << uint32(0x0); // 10
			data << uint32(2502) << uint32(0x0); // 11
			data << uint32(2493) << uint32(0x0); // 12
			data << uint32(2491) << uint32(0x0); // 13

			data << uint32(2495) << uint32(0x0); // 14
			data << uint32(2494) << uint32(0x0); // 15
			data << uint32(2497) << uint32(0x0); // 16

			data << uint32(2762) << uint32(0x0); // 17
			data << uint32(2662) << uint32(0x0); // 18
			data << uint32(2663) << uint32(0x0); // 19
			data << uint32(2664) << uint32(0x0); // 20

			data << uint32(2760) << uint32(0x0); // 21
			data << uint32(2670) << uint32(0x0); // 22
			data << uint32(2668) << uint32(0x0); // 23
			data << uint32(2669) << uint32(0x0); // 24

			data << uint32(2761) << uint32(0x0); // 25
			data << uint32(2667) << uint32(0x0); // 26
			data << uint32(2665) << uint32(0x0); // 27
			data << uint32(2666) << uint32(0x0); // 28

			data << uint32(2763) << uint32(0x0); // 29
			data << uint32(2659) << uint32(0x0); // 30
			data << uint32(2660) << uint32(0x0); // 31
			data << uint32(2661) << uint32(0x0); // 32

			data << uint32(2671) << uint32(0x0); // 33
			data << uint32(2676) << uint32(0x0); // 34
			data << uint32(2677) << uint32(0x0); // 35
			data << uint32(2672) << uint32(0x0); // 36
			data << uint32(2673) << uint32(0x0); // 37
		}
		break;
	case 3519: // Terokkar Forest
		if (pvp && pvp->GetTypeId() == OUTDOOR_PVP_TF)
			pvp->FillInitialWorldStates(data);
		else {
			data << uint32(0xa41) << uint32(0x0); // 10 // 2625 capture bar pos
			data << uint32(0xa40) << uint32(0x14); // 11 // 2624 capture bar neutral
			data << uint32(0xa3f) << uint32(0x0); // 12 // 2623 show capture bar
			data << uint32(0xa3e) << uint32(0x0); // 13 // 2622 horde towers controlled
			data << uint32(0xa3d) << uint32(0x5); // 14 // 2621 ally towers controlled
			data << uint32(0xa3c) << uint32(0x0); // 15 // 2620 show towers controlled
			data << uint32(0xa88) << uint32(0x0); // 16 // 2696 SE Neu
			data << uint32(0xa87) << uint32(0x0); // 17 // SE Horde
			data << uint32(0xa86) << uint32(0x0); // 18 // SE Ally
			data << uint32(0xa85) << uint32(0x0); // 19 //S Neu
			data << uint32(0xa84) << uint32(0x0); // 20 S Horde
			data << uint32(0xa83) << uint32(0x0); // 21 S Ally
			data << uint32(0xa82) << uint32(0x0); // 22 NE Neu
			data << uint32(0xa81) << uint32(0x0); // 23 NE Horde
			data << uint32(0xa80) << uint32(0x0); // 24 NE Ally
			data << uint32(0xa7e) << uint32(0x0); // 25 // 2686 N Neu
			data << uint32(0xa7d) << uint32(0x0); // 26 N Horde
			data << uint32(0xa7c) << uint32(0x0); // 27 N Ally
			data << uint32(0xa7b) << uint32(0x0); // 28 NW Ally
			data << uint32(0xa7a) << uint32(0x0); // 29 NW Horde
			data << uint32(0xa79) << uint32(0x0); // 30 NW Neutral
			data << uint32(0x9d0) << uint32(0x5); // 31 // 2512 locked time remaining seconds first digit
			data << uint32(0x9ce) << uint32(0x0); // 32 // 2510 locked time remaining seconds second digit
			data << uint32(0x9cd) << uint32(0x0); // 33 // 2509 locked time remaining minutes
			data << uint32(0x9cc) << uint32(0x0); // 34 // 2508 neutral locked time show
			data << uint32(0xad0) << uint32(0x0); // 35 // 2768 horde locked time show
			data << uint32(0xacf) << uint32(0x1); // 36 // 2767 ally locked time show
		}
		break;
	case 3521: // Zangarmarsh
		if (pvp && pvp->GetTypeId() == OUTDOOR_PVP_ZM)
			pvp->FillInitialWorldStates(data);
		else {
			data << uint32(0x9e1) << uint32(0x0); // 10 //2529
			data << uint32(0x9e0) << uint32(0x0); // 11
			data << uint32(0x9df) << uint32(0x0); // 12
			data << uint32(0xa5d) << uint32(0x1); // 13 //2653
			data << uint32(0xa5c) << uint32(0x0); // 14 //2652 east beacon neutral
			data << uint32(0xa5b) << uint32(0x1); // 15 horde
			data << uint32(0xa5a) << uint32(0x0); // 16 ally
			data << uint32(0xa59) << uint32(0x1); // 17 // 2649 Twin spire graveyard horde  12???
			data << uint32(0xa58) << uint32(0x0); // 18 ally     14 ???
			data << uint32(0xa57) << uint32(0x0); // 19 neutral  7???
			data << uint32(0xa56) << uint32(0x0); // 20 // 2646 west beacon neutral
			data << uint32(0xa55) << uint32(0x1); // 21 horde
			data << uint32(0xa54) << uint32(0x0); // 22 ally
			data << uint32(0x9e7) << uint32(0x0); // 23 // 2535
			data << uint32(0x9e6) << uint32(0x0); // 24
			data << uint32(0x9e5) << uint32(0x0); // 25
			data << uint32(0xa00) << uint32(0x0); // 26 // 2560
			data << uint32(0x9ff) << uint32(0x1); // 27
			data << uint32(0x9fe) << uint32(0x0); // 28
			data << uint32(0x9fd) << uint32(0x0); // 29
			data << uint32(0x9fc) << uint32(0x1); // 30
			data << uint32(0x9fb) << uint32(0x0); // 31
			data << uint32(0xa62) << uint32(0x0); // 32 // 2658
			data << uint32(0xa61) << uint32(0x1); // 33
			data << uint32(0xa60) << uint32(0x1); // 34
			data << uint32(0xa5f) << uint32(0x0); // 35
		}
		break;
	case 3698: // Nagrand Arena
		if (bg && bg->GetTypeID(true) == BATTLEGROUND_NA)
			bg->FillInitialWorldStates(data);
		else {
			data << uint32(0xa0f) << uint32(0x0); // 7
			data << uint32(0xa10) << uint32(0x0); // 8
			data << uint32(0xa11) << uint32(0x0); // 9 show
		}
		break;
	case 3702: // Blade's Edge Arena
		if (bg && bg->GetTypeID(true) == BATTLEGROUND_BE)
			bg->FillInitialWorldStates(data);
		else {
			data << uint32(0x9f0) << uint32(0x0); // 7 gold
			data << uint32(0x9f1) << uint32(0x0); // 8 green
			data << uint32(0x9f3) << uint32(0x0); // 9 show
		}
		break;
	case 3968: // Ruins of Lordaeron
		if (bg && bg->GetTypeID(true) == BATTLEGROUND_RL)
			bg->FillInitialWorldStates(data);
		else {
			data << uint32(0xbb8) << uint32(0x0); // 7 gold
			data << uint32(0xbb9) << uint32(0x0); // 8 green
			data << uint32(0xbba) << uint32(0x0); // 9 show
		}
		break;
	case 4378: // Dalaran Sewers
		if (bg && bg->GetTypeID(true) == BATTLEGROUND_DS)
			bg->FillInitialWorldStates(data);
		else {
			data << uint32(3601) << uint32(0x0); // 7 gold
			data << uint32(3600) << uint32(0x0); // 8 green
			data << uint32(3610) << uint32(0x0); // 9 show
		}
		break;
	case 3703: // Shattrath City
	case 4384: // Strand of the Ancients
		if (bg && bg->GetTypeID(true) == BATTLEGROUND_SA)
			bg->FillInitialWorldStates(data);
		else {
			// 1-3 A defend, 4-6 H defend, 7-9 unk defend, 1 - ok, 2 - half destroyed, 3 - destroyed
			data << uint32(0xf09) << uint32(0x0); // 7  3849 Gate of Temple
			data << uint32(0xe36) << uint32(0x0); // 8  3638 Gate of Yellow Moon
			data << uint32(0xe27) << uint32(0x0); // 9  3623 Gate of Green Emerald
			data << uint32(0xe24) << uint32(0x0); // 10 3620 Gate of Blue Sapphire
			data << uint32(0xe21) << uint32(0x0); // 11 3617 Gate of Red Sun
			data << uint32(0xe1e) << uint32(0x0); // 12 3614 Gate of Purple Ametyst

			data << uint32(0xdf3) << uint32(0x0); // 13 3571 bonus timer (1 - on, 0 - off)
			data << uint32(0xded) << uint32(0x0); // 14 3565 Horde Attacker
			data << uint32(0xdec) << uint32(0x0); // 15 3564 Alliance Attacker
			// End Round (timer), better explain this by example, eg. ends in 19:59 -> A:BC
			data << uint32(0xde9) << uint32(0x0); // 16 3561 C
			data << uint32(0xde8) << uint32(0x0); // 17 3560 B
			data << uint32(0xde7) << uint32(0x0); // 18 3559 A
			data << uint32(0xe35) << uint32(0x0); // 19 3637 East g - Horde control
			data << uint32(0xe34) << uint32(0x0); // 20 3636 West g - Horde control
			data << uint32(0xe33) << uint32(0x0); // 21 3635 South g - Horde control
			data << uint32(0xe32) << uint32(0x0); // 22 3634 East g - Alliance control
			data << uint32(0xe31) << uint32(0x0); // 23 3633 West g - Alliance control
			data << uint32(0xe30) << uint32(0x0); // 24 3632 South g - Alliance control
			data << uint32(0xe2f) << uint32(0x0); // 25 3631 Chamber of Ancients - Horde control
			data << uint32(0xe2e) << uint32(0x0); // 26 3630 Chamber of Ancients - Alliance control
			data << uint32(0xe2d) << uint32(0x0); // 27 3629 Beach1 - Horde control
			data << uint32(0xe2c) << uint32(0x0); // 28 3628 Beach2 - Horde control
			data << uint32(0xe2b) << uint32(0x0); // 29 3627 Beach1 - Alliance control
			data << uint32(0xe2a) << uint32(0x0); // 30 3626 Beach2 - Alliance control
			// and many unks...
		}
		break;
	case 4406: // Ring of Valor
		if (bg && bg->GetTypeID(true) == BATTLEGROUND_RV)
			bg->FillInitialWorldStates(data);
		else {
			data << uint32(0xe10) << uint32(0x0); // 7 gold
			data << uint32(0xe11) << uint32(0x0); // 8 green
			data << uint32(0xe1a) << uint32(0x0); // 9 show
		}
		break;
	case 4710: // Isle of Conquest
		if (bg && bg->GetTypeID(true) == BATTLEGROUND_IC)
			bg->FillInitialWorldStates(data);
		else {
			data << uint32(4221) << uint32(1); // 7
			data << uint32(4222) << uint32(1); // 8
			data << uint32(4226) << uint32(300); // 9
			data << uint32(4227) << uint32(300); // 10
			data << uint32(4322) << uint32(1); // 11
			data << uint32(4321) << uint32(1); // 12
			data << uint32(4320) << uint32(1); // 13
			data << uint32(4323) << uint32(1); // 14
			data << uint32(4324) << uint32(1); // 15
			data << uint32(4325) << uint32(1); // 16
			data << uint32(4317) << uint32(1); // 17

			data << uint32(4301) << uint32(1); // 18
			data << uint32(4296) << uint32(1); // 19
			data << uint32(4306) << uint32(1); // 20
			data << uint32(4311) << uint32(1); // 21
			data << uint32(4294) << uint32(1); // 22
			data << uint32(4243) << uint32(1); // 23
			data << uint32(4345) << uint32(1); // 24
		}
		break;
	case 5031: // Twin Peaks
		if (bg && bg->GetTypeID(true) == BATTLEGROUND_TP)
			bg->FillInitialWorldStates(data);
		else {
			data << uint32(0x62d) << uint32(0x0); // 7 1581 alliance flag captures
			data << uint32(0x62e) << uint32(0x0); // 8 1582 horde flag captures
			data << uint32(0x609) << uint32(0x0); // 9 1545 unk, set to 1 on alliance flag pickup...
			data << uint32(0x60a) << uint32(0x0); // 10 1546 unk, set to 1 on horde flag pickup, after drop it's -1
			data << uint32(0x60b) << uint32(0x2); // 11 1547 unk
			data << uint32(0x641) << uint32(0x3); // 12 1601 unk (max flag captures?)
			data << uint32(0x922) << uint32(0x1); // 13 2338 horde (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
			data << uint32(0x923) << uint32(0x1); // 14 2339 alliance (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
		}
		break;
	case 5449:
		if (bg && bg->GetTypeID(true) == BATTLEGROUND_BG)
			bg->FillInitialWorldStates(data);
		break;

	default:
		data << uint32(0x914) << uint32(0x0); // 7
		data << uint32(0x913) << uint32(0x0); // 8
		data << uint32(0x912) << uint32(0x0); // 9
		data << uint32(0x915) << uint32(0x0); // 10
		break;
	}
	GetSession()->SendPacket(&data);
	SendBGWeekendWorldStates();
}

void Player::SendBGWeekendWorldStates() {
	for (uint32 i = 1; i < sBattlemasterListStore.GetNumRows(); ++i) {
		BattlemasterListEntry const * bl = sBattlemasterListStore.LookupEntry(
				i);
		if (bl && bl->HolidayWorldStateId) {
			if (BattlegroundMgr::IsBGWeekend((BattlegroundTypeId) bl->id))
				SendUpdateWorldState(bl->HolidayWorldStateId, 1);
			else
				SendUpdateWorldState(bl->HolidayWorldStateId, 0);
		}
	}
}

uint32 Player::GetXPRestBonus(uint32 xp) {
	uint32 rested_bonus = (uint32) GetRestBonus(); // xp for each rested bonus

	if (rested_bonus > xp) // max rested_bonus == xp or (r+x) = 200% xp
		rested_bonus = xp;

	SetRestBonus(GetRestBonus() - rested_bonus);

	sLog->outDetail("Player gain %u xp (+ %u Rested Bonus). Rested points=%f",
			xp + rested_bonus, rested_bonus, GetRestBonus());
	return rested_bonus;
}

void Player::SetBindPoint(uint64 guid) {
	WorldPacket data(SMSG_BINDER_CONFIRM, 8);
	data << uint64(guid);
	GetSession()->SendPacket(&data);
}

void Player::SendTalentWipeConfirm(uint64 guid) {
	WorldPacket data(MSG_TALENT_WIPE_CONFIRM, (8 + 4));
	data << uint64(guid);
	uint32 cost =
			sWorld->getBoolConfig(CONFIG_NO_RESET_TALENT_COST) ?
					0 : resetTalentsCost();
	data << cost;
	GetSession()->SendPacket(&data);
}

void Player::ResetPetTalents() {
	// This needs another gossip option + NPC text as a confirmation.
	// The confirmation gossip listid has the text: "Yes, please do."
	Pet* pet = GetPet();

	if (!pet || pet->getPetType() != HUNTER_PET || pet->m_usedTalentCount == 0)
		return;

	CharmInfo *charmInfo = pet->GetCharmInfo();
	if (!charmInfo) {
		sLog->outError(
				"Object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!",
				pet->GetGUIDLow(), pet->GetTypeId());
		return;
	}
	pet->resetTalents();
	SendTalentsInfoData(true);
}

/*********************************************************/
/***                    STORAGE SYSTEM                 ***/
/*********************************************************/

void Player::SetVirtualItemSlot(uint8 i, Item* item) {
	ASSERT(i < 3);
	if (i < 2 && item) {
		if (!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
			return;
		uint32 charges = item->GetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT);
		if (charges == 0)
			return;
		if (charges > 1)
			item->SetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT, charges - 1);
		else if (charges <= 1) {
			ApplyEnchantment(item, TEMP_ENCHANTMENT_SLOT, false);
			item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT);
		}
	}
}

void Player::SetSheath(SheathState sheathed) {
	switch (sheathed) {
	case SHEATH_STATE_UNARMED: // no prepared weapon
		SetVirtualItemSlot(0, NULL);
		SetVirtualItemSlot(1, NULL);
		SetVirtualItemSlot(2, NULL);
		break;
	case SHEATH_STATE_MELEE: // prepared melee weapon
	{
		SetVirtualItemSlot(0, GetWeaponForAttack(BASE_ATTACK, true));
		SetVirtualItemSlot(1, GetWeaponForAttack(OFF_ATTACK, true));
		SetVirtualItemSlot(2, NULL);
	}
		;
		break;
	case SHEATH_STATE_RANGED: // prepared ranged weapon
		SetVirtualItemSlot(0, NULL);
		SetVirtualItemSlot(1, NULL);
		SetVirtualItemSlot(2, GetWeaponForAttack(RANGED_ATTACK, true));
		break;
	default:
		SetVirtualItemSlot(0, NULL);
		SetVirtualItemSlot(1, NULL);
		SetVirtualItemSlot(2, NULL);
		break;
	}
	Unit::SetSheath(sheathed); // this must visualize Sheath changing for other players...
}

uint8 Player::FindEquipSlot(ItemPrototype const* proto, uint32 slot,
		bool swap) const {
	uint8 pClass = getClass();

	uint8 slots[4];
	slots[0] = NULL_SLOT;
	slots[1] = NULL_SLOT;
	slots[2] = NULL_SLOT;
	slots[3] = NULL_SLOT;
	switch (proto->InventoryType) {
	case INVTYPE_HEAD:
		slots[0] = EQUIPMENT_SLOT_HEAD;
		break;
	case INVTYPE_NECK:
		slots[0] = EQUIPMENT_SLOT_NECK;
		break;
	case INVTYPE_SHOULDERS:
		slots[0] = EQUIPMENT_SLOT_SHOULDERS;
		break;
	case INVTYPE_BODY:
		slots[0] = EQUIPMENT_SLOT_BODY;
		break;
	case INVTYPE_CHEST:
		slots[0] = EQUIPMENT_SLOT_CHEST;
		break;
	case INVTYPE_ROBE:
		slots[0] = EQUIPMENT_SLOT_CHEST;
		break;
	case INVTYPE_WAIST:
		slots[0] = EQUIPMENT_SLOT_WAIST;
		break;
	case INVTYPE_LEGS:
		slots[0] = EQUIPMENT_SLOT_LEGS;
		break;
	case INVTYPE_FEET:
		slots[0] = EQUIPMENT_SLOT_FEET;
		break;
	case INVTYPE_WRISTS:
		slots[0] = EQUIPMENT_SLOT_WRISTS;
		break;
	case INVTYPE_HANDS:
		slots[0] = EQUIPMENT_SLOT_HANDS;
		break;
	case INVTYPE_FINGER:
		slots[0] = EQUIPMENT_SLOT_FINGER1;
		slots[1] = EQUIPMENT_SLOT_FINGER2;
		break;
	case INVTYPE_TRINKET:
		slots[0] = EQUIPMENT_SLOT_TRINKET1;
		slots[1] = EQUIPMENT_SLOT_TRINKET2;
		break;
	case INVTYPE_CLOAK:
		slots[0] = EQUIPMENT_SLOT_BACK;
		break;
	case INVTYPE_WEAPON: {
		slots[0] = EQUIPMENT_SLOT_MAINHAND;

		// suggest offhand slot only if know dual wielding
		// (this will be replace mainhand weapon at auto equip instead unwonted "you don't known dual wielding" ...
		if (CanDualWield())
			slots[1] = EQUIPMENT_SLOT_OFFHAND;
		break;
		break;
	}
		;
	case INVTYPE_SHIELD:
		slots[0] = EQUIPMENT_SLOT_OFFHAND;
		break;
	case INVTYPE_RANGED:
		slots[0] = EQUIPMENT_SLOT_RANGED;
		break;
	case INVTYPE_2HWEAPON:
		slots[0] = EQUIPMENT_SLOT_MAINHAND;
		if (Item *mhWeapon = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND))
			if (ItemPrototype const *mhWeaponProto = mhWeapon->GetProto())
				if (mhWeaponProto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM
						|| mhWeaponProto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF) {
					const_cast<Player *>(this)->AutoUnequipOffhandIfNeed(true);
					break;
				}
		if (CanDualWield() && CanTitanGrip()
				&& proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM
				&& proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF)
			slots[1] = EQUIPMENT_SLOT_OFFHAND;
		break;
	case INVTYPE_TABARD:
		slots[0] = EQUIPMENT_SLOT_TABARD;
		break;
	case INVTYPE_WEAPONMAINHAND:
		slots[0] = EQUIPMENT_SLOT_MAINHAND;
		break;
	case INVTYPE_WEAPONOFFHAND:
		slots[0] = EQUIPMENT_SLOT_OFFHAND;
		break;
	case INVTYPE_HOLDABLE:
		slots[0] = EQUIPMENT_SLOT_OFFHAND;
		break;
	case INVTYPE_THROWN:
		slots[0] = EQUIPMENT_SLOT_RANGED;
		break;
	case INVTYPE_RANGEDRIGHT:
		slots[0] = EQUIPMENT_SLOT_RANGED;
		break;
	case INVTYPE_BAG:
		slots[0] = INVENTORY_SLOT_BAG_START + 0;
		slots[1] = INVENTORY_SLOT_BAG_START + 1;
		slots[2] = INVENTORY_SLOT_BAG_START + 2;
		slots[3] = INVENTORY_SLOT_BAG_START + 3;
		break;
	case INVTYPE_RELIC: {
		switch (proto->SubClass) {
		case ITEM_SUBCLASS_ARMOR_MISC:
			if (pClass == CLASS_WARLOCK)
				slots[0] = EQUIPMENT_SLOT_RANGED;
			break;
		case ITEM_SUBCLASS_ARMOR_RELIC:
			if (pClass == CLASS_PALADIN || pClass == CLASS_DRUID
					|| pClass == CLASS_SHAMAN || pClass == CLASS_DEATH_KNIGHT)
				slots[0] = EQUIPMENT_SLOT_RANGED;
			break;
		}
		break;
	}
	default:
		return NULL_SLOT;
	}

	if (slot != NULL_SLOT) {
		if (swap || !GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
			for (uint8 i = 0; i < 4; ++i)
				if (slots[i] == slot)
					return slot;
	} else {
		// search free slot at first
		for (uint8 i = 0; i < 4; ++i)
			if (slots[i] != NULL_SLOT
					&& !GetItemByPos(INVENTORY_SLOT_BAG_0, slots[i]))
				// in case 2hand equipped weapon (without titan grip) offhand slot empty but not free
				if (slots[i] != EQUIPMENT_SLOT_OFFHAND || !IsTwoHandUsed())
					return slots[i];

		// if not found free and can swap return first appropriate from used
		for (uint8 i = 0; i < 4; ++i)
			if (slots[i] != NULL_SLOT && swap)
				return slots[i];
	}

	// no free position
	return NULL_SLOT;
}

uint8 Player::CanUnequipItems(uint32 item, uint32 count) const {
	uint32 tempcount = 0;

	uint8 res = EQUIP_ERR_OK;

	for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i)
		if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem->GetEntry() == item) {
				uint8 ires = CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i,
						false);
				if (ires == EQUIP_ERR_OK) {
					tempcount += pItem->GetCount();
					if (tempcount >= count)
						return EQUIP_ERR_OK;
				} else
					res = ires;
			}

	for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
		if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem->GetEntry() == item) {
				tempcount += pItem->GetCount();
				if (tempcount >= count)
					return EQUIP_ERR_OK;
			}

	for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
		if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem->GetEntry() == item) {
				tempcount += pItem->GetCount();
				if (tempcount >= count)
					return EQUIP_ERR_OK;
			}

	for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
		if (Bag *pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
				if (Item *pItem = GetItemByPos(i, j))
					if (pItem->GetEntry() == item) {
						tempcount += pItem->GetCount();
						if (tempcount >= count)
							return EQUIP_ERR_OK;
					}

	// not found req. item count and have unequippable items
	return res;
}

uint32 Player::GetItemCount(uint32 item, bool inBankAlso,
		Item* skipItem) const {
	uint32 count = 0;
	for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
		if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem != skipItem && pItem->GetEntry() == item)
				count += pItem->GetCount();

	for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
		if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem != skipItem && pItem->GetEntry() == item)
				count += pItem->GetCount();

	for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
		if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			count += pBag->GetItemCount(item, skipItem);

	if (skipItem && skipItem->GetProto()->GemProperties)
		for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
			if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
				if (pItem != skipItem && pItem->GetProto()->Socket[0].Color)
					count += pItem->GetGemCountWithID(item);

	if (inBankAlso) {
		for (uint8 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i)
			if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
				if (pItem != skipItem && pItem->GetEntry() == item)
					count += pItem->GetCount();

		for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
			if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
				count += pBag->GetItemCount(item, skipItem);

		if (skipItem && skipItem->GetProto()->GemProperties)
			for (uint8 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i)
				if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
					if (pItem != skipItem && pItem->GetProto()->Socket[0].Color)
						count += pItem->GetGemCountWithID(item);
	}

	return count;
}

uint32 Player::GetItemCountWithLimitCategory(uint32 limitCategory,
		Item* skipItem) const {
	uint32 count = 0;
	for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
		if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem != skipItem)
				if (ItemPrototype const *pProto = pItem->GetProto())
					if (pProto->ItemLimitCategory == limitCategory)
						count += pItem->GetCount();

	for (int i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
		if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem != skipItem)
				if (ItemPrototype const *pProto = pItem->GetProto())
					if (pProto->ItemLimitCategory == limitCategory)
						count += pItem->GetCount();

	for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
		if (Bag* pBag = (Bag*) GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			count += pBag->GetItemCountWithLimitCategory(limitCategory,
					skipItem);

	for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; ++i)
		if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem != skipItem)
				if (ItemPrototype const *pProto = pItem->GetProto())
					if (pProto->ItemLimitCategory == limitCategory)
						count += pItem->GetCount();

	for (int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
		if (Bag* pBag = (Bag*) GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			count += pBag->GetItemCountWithLimitCategory(limitCategory,
					skipItem);

	return count;
}

Item* Player::GetItemByGuid(uint64 guid) const {
	for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
		if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem->GetGUID() == guid)
				return pItem;

	for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
		if (Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem->GetGUID() == guid)
				return pItem;

	for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
		if (Bag *pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
				if (Item* pItem = pBag->GetItemByPos(j))
					if (pItem->GetGUID() == guid)
						return pItem;

	for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
		if (Bag *pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
				if (Item* pItem = pBag->GetItemByPos(j))
					if (pItem->GetGUID() == guid)
						return pItem;

	return NULL;
}

Item* Player::GetItemByPos(uint16 pos) const {
	uint8 bag = pos >> 8;
	uint8 slot = pos & 255;
	return GetItemByPos(bag, slot);
}

Item* Player::GetItemByPos(uint8 bag, uint8 slot) const {
	if (bag == INVENTORY_SLOT_BAG_0
			&& (slot < BANK_SLOT_BAG_END
					|| (slot >= KEYRING_SLOT_START
							&& slot < CURRENCYTOKEN_SLOT_END)))
		return m_items[slot];
	else if ((bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END)
			|| (bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END)) {
		if (Bag *pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, bag))
			return pBag->GetItemByPos(slot);
	}
	return NULL;
}

Item* Player::GetWeaponForAttack(WeaponAttackType attackType,
		bool useable /*= false*/) const {
	uint8 slot;
	switch (attackType) {
	case BASE_ATTACK:
		slot = EQUIPMENT_SLOT_MAINHAND;
		break;
	case OFF_ATTACK:
		slot = EQUIPMENT_SLOT_OFFHAND;
		break;
	case RANGED_ATTACK:
		slot = EQUIPMENT_SLOT_RANGED;
		break;
	default:
		return NULL;
	}

	Item* item = NULL;
	if (useable)
		item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, slot);
	else
		item = GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
	if (!item || item->GetProto()->Class != ITEM_CLASS_WEAPON)
		return NULL;

	if (!useable)
		return item;

	if (item->IsBroken() || IsInFeralForm())
		return NULL;

	return item;
}

Item* Player::GetShield(bool useable) const {
	Item* item = NULL;
	if (useable)
		item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0,
				EQUIPMENT_SLOT_OFFHAND);
	else
		item = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
	if (!item || item->GetProto()->Class != ITEM_CLASS_ARMOR)
		return NULL;

	if (!useable)
		return item;

	if (item->IsBroken())
		return NULL;

	return item;
}

uint8 Player::GetAttackBySlot(uint8 slot) {
	switch (slot) {
	case EQUIPMENT_SLOT_MAINHAND:
		return BASE_ATTACK;
	case EQUIPMENT_SLOT_OFFHAND:
		return OFF_ATTACK;
	case EQUIPMENT_SLOT_RANGED:
		return RANGED_ATTACK;
	default:
		return MAX_ATTACK;
	}
}

bool Player::IsInventoryPos(uint8 bag, uint8 slot) {
	if (bag == INVENTORY_SLOT_BAG_0 && slot == NULL_SLOT)
		return true;
	if (bag == INVENTORY_SLOT_BAG_0
			&& (slot >= INVENTORY_SLOT_ITEM_START
					&& slot < INVENTORY_SLOT_ITEM_END))
		return true;
	if (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END)
		return true;
	if (bag == INVENTORY_SLOT_BAG_0
			&& (slot >= KEYRING_SLOT_START && slot < CURRENCYTOKEN_SLOT_END))
		return true;
	return false;
}

bool Player::IsEquipmentPos(uint8 bag, uint8 slot) {
	if (bag == INVENTORY_SLOT_BAG_0 && (slot < EQUIPMENT_SLOT_END))
		return true;
	if (bag == INVENTORY_SLOT_BAG_0
			&& (slot >= INVENTORY_SLOT_BAG_START
					&& slot < INVENTORY_SLOT_BAG_END))
		return true;
	return false;
}

bool Player::IsBankPos(uint8 bag, uint8 slot) {
	if (bag == INVENTORY_SLOT_BAG_0
			&& (slot >= BANK_SLOT_ITEM_START && slot < BANK_SLOT_ITEM_END))
		return true;
	if (bag == INVENTORY_SLOT_BAG_0
			&& (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END))
		return true;
	if (bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END)
		return true;
	return false;
}

bool Player::IsBagPos(uint16 pos) {
	uint8 bag = pos >> 8;
	uint8 slot = pos & 255;
	if (bag == INVENTORY_SLOT_BAG_0
			&& (slot >= INVENTORY_SLOT_BAG_START
					&& slot < INVENTORY_SLOT_BAG_END))
		return true;
	if (bag == INVENTORY_SLOT_BAG_0
			&& (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END))
		return true;
	return false;
}

bool Player::IsValidPos(uint8 bag, uint8 slot, bool explicit_pos) {
	// post selected
	if (bag == NULL_BAG && !explicit_pos)
		return true;

	if (bag == INVENTORY_SLOT_BAG_0) {
		// any post selected
		if (slot == NULL_SLOT && !explicit_pos)
			return true;

		// equipment
		if (slot < EQUIPMENT_SLOT_END)
			return true;

		// bag equip slots
		if (slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END)
			return true;

		// backpack slots
		if (slot >= INVENTORY_SLOT_ITEM_START && slot < INVENTORY_SLOT_ITEM_END)
			return true;

		// keyring slots
		if (slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_END)
			return true;

		// bank main slots
		if (slot >= BANK_SLOT_ITEM_START && slot < BANK_SLOT_ITEM_END)
			return true;

		// bank bag slots
		if (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END)
			return true;

		return false;
	}

	// bag content slots
	if (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END) {
		Bag* pBag = (Bag*) GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
		if (!pBag)
			return false;

		// any post selected
		if (slot == NULL_SLOT && !explicit_pos)
			return true;

		return slot < pBag->GetBagSize();
	}

	// bank bag content slots
	if (bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END) {
		Bag* pBag = (Bag*) GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
		if (!pBag)
			return false;

		// any post selected
		if (slot == NULL_SLOT && !explicit_pos)
			return true;

		return slot < pBag->GetBagSize();
	}

	// where this?
	return false;
}

bool Player::HasItemCount(uint32 item, uint32 count, bool inBankAlso) const {
	uint32 tempcount = 0;
	for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++) {
		Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
		if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) {
			tempcount += pItem->GetCount();
			if (tempcount >= count)
				return true;
		}
	}
	for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i) {
		Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
		if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) {
			tempcount += pItem->GetCount();
			if (tempcount >= count)
				return true;
		}
	}
	for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) {
		if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) {
			for (uint32 j = 0; j < pBag->GetBagSize(); j++) {
				Item* pItem = GetItemByPos(i, j);
				if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) {
					tempcount += pItem->GetCount();
					if (tempcount >= count)
						return true;
				}
			}
		}
	}

	if (inBankAlso) {
		for (uint8 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++) {
			Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
			if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) {
				tempcount += pItem->GetCount();
				if (tempcount >= count)
					return true;
			}
		}
		for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++) {
			if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) {
				for (uint32 j = 0; j < pBag->GetBagSize(); j++) {
					Item* pItem = GetItemByPos(i, j);
					if (pItem && pItem->GetEntry() == item
							&& !pItem->IsInTrade()) {
						tempcount += pItem->GetCount();
						if (tempcount >= count)
							return true;
					}
				}
			}
		}
	}

	return false;
}

bool Player::HasItemOrGemWithIdEquipped(uint32 item, uint32 count,
		uint8 except_slot) const {
	uint32 tempcount = 0;
	for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) {
		if (i == except_slot)
			continue;

		Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
		if (pItem && pItem->GetEntry() == item) {
			tempcount += pItem->GetCount();
			if (tempcount >= count)
				return true;
		}
	}

	ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(item);
	if (pProto && pProto->GemProperties) {
		for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) {
			if (i == except_slot)
				continue;

			Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
			if (pItem && pItem->GetProto()->Socket[0].Color) {
				tempcount += pItem->GetGemCountWithID(item);
				if (tempcount >= count)
					return true;
			}
		}
	}

	return false;
}

bool Player::HasItemOrGemWithLimitCategoryEquipped(uint32 limitCategory,
		uint32 count, uint8 except_slot) const {
	uint32 tempcount = 0;
	for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) {
		if (i == except_slot)
			continue;

		Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
		if (!pItem)
			continue;

		ItemPrototype const *pProto = pItem->GetProto();
		if (!pProto)
			continue;

		if (pProto->ItemLimitCategory == limitCategory) {
			tempcount += pItem->GetCount();
			if (tempcount >= count)
				return true;
		}

		if (pProto->Socket[0].Color
				|| pItem->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT)) {
			tempcount += pItem->GetGemCountWithLimitCategory(limitCategory);
			if (tempcount >= count)
				return true;
		}
	}

	return false;
}

uint8 Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem,
		uint32* no_space_count) const {
	ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(entry);
	if (!pProto) {
		if (no_space_count)
			*no_space_count = count;
		return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
	}

	if (pItem && pItem->m_lootGenerated)
		return EQUIP_ERR_ALREADY_LOOTED;

	// no maximum
	if ((pProto->MaxCount <= 0 && pProto->ItemLimitCategory == 0)
			|| pProto->MaxCount == 2147483647)
		return EQUIP_ERR_OK;

	if (pProto->MaxCount > 0) {
		uint32 curcount = GetItemCount(pProto->ItemId, true, pItem);
		if (curcount + count > uint32(pProto->MaxCount)) {
			if (no_space_count)
				*no_space_count = count + curcount - pProto->MaxCount;
			return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
		}
	}

	// check unique-equipped limit
	if (pProto->ItemLimitCategory) {
		ItemLimitCategoryEntry const* limitEntry =
				sItemLimitCategoryStore.LookupEntry(pProto->ItemLimitCategory);
		if (!limitEntry) {
			if (no_space_count)
				*no_space_count = count;
			return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
		}

		if (limitEntry->mode == ITEM_LIMIT_CATEGORY_MODE_HAVE) {
			uint32 curcount = GetItemCountWithLimitCategory(
					pProto->ItemLimitCategory, pItem);
			if (curcount + count > uint32(limitEntry->maxCount)) {
				if (no_space_count)
					*no_space_count = count + curcount - limitEntry->maxCount;
				return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED;
			}
		}
	}

	return EQUIP_ERR_OK;
}

bool Player::HasItemTotemCategory(uint32 TotemCategory) const {
	Item *pItem;
	for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) {
		pItem = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, i);
		if (pItem
				&& IsTotemCategoryCompatiableWith(
						pItem->GetProto()->TotemCategory, TotemCategory))
			return true;
	}
	for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i) {
		pItem = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, i);
		if (pItem
				&& IsTotemCategoryCompatiableWith(
						pItem->GetProto()->TotemCategory, TotemCategory))
			return true;
	}
	for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) {
		if (Bag *pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) {
			for (uint32 j = 0; j < pBag->GetBagSize(); ++j) {
				pItem = GetUseableItemByPos(i, j);
				if (pItem
						&& IsTotemCategoryCompatiableWith(
								pItem->GetProto()->TotemCategory, TotemCategory))
					return true;
			}
		}
	}
	return false;
}

uint8 Player::_CanStoreItem_InSpecificSlot(uint8 bag, uint8 slot,
		ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count,
		bool swap, Item* pSrcItem) const {
	Item* pItem2 = GetItemByPos(bag, slot);

	// ignore move item (this slot will be empty at move)
	if (pItem2 == pSrcItem)
		pItem2 = NULL;

	uint32 need_space;

	if (pSrcItem && pSrcItem->IsBag() && !((Bag*) pSrcItem)->IsEmpty()
			&& !IsBagPos(uint16(bag) << 8 | slot))
		return EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS;

	// empty specific slot - check item fit to slot
	if (!pItem2 || swap) {
		if (bag == INVENTORY_SLOT_BAG_0) {
			// keyring case
			if (slot >= KEYRING_SLOT_START
					&& slot < KEYRING_SLOT_START + GetMaxKeyringSize()
					&& !(pProto->BagFamily & BAG_FAMILY_MASK_KEYS))
				return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;

			// currencytoken case
			if (slot >= CURRENCYTOKEN_SLOT_START
					&& slot < CURRENCYTOKEN_SLOT_END
					&& !(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS))
				return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;

			// prevent cheating
			if ((slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END)
					|| slot >= PLAYER_SLOT_END)
				return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
		} else {
			Bag* pBag = (Bag*) GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
			if (!pBag)
				return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;

			ItemPrototype const* pBagProto = pBag->GetProto();
			if (!pBagProto)
				return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;

			if (slot >= pBagProto->ContainerSlots)
				return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;

			if (!ItemCanGoIntoBag(pProto, pBagProto))
				return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
		}

		// non empty stack with space
		need_space = pProto->GetMaxStackSize();
	}
	// non empty slot, check item type
	else {
		// can be merged at least partly
		uint8 res = pItem2->CanBeMergedPartlyWith(pProto);
		if (res != EQUIP_ERR_OK)
			return res;

		// free stack space or infinity
		need_space = pProto->GetMaxStackSize() - pItem2->GetCount();
	}

	if (need_space > count)
		need_space = count;

	ItemPosCount newPosition = ItemPosCount((bag << 8) | slot, need_space);
	if (!newPosition.isContainedIn(dest)) {
		dest.push_back(newPosition);
		count -= need_space;
	}
	return EQUIP_ERR_OK;
}

uint8 Player::_CanStoreItem_InBag(uint8 bag, ItemPosCountVec &dest,
		ItemPrototype const *pProto, uint32& count, bool merge,
		bool non_specialized, Item* pSrcItem, uint8 skip_bag,
		uint8 skip_slot) const {
	// skip specific bag already processed in first called _CanStoreItem_InBag
	if (bag == skip_bag)
		return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;

	// skip not existed bag or self targeted bag
	Bag* pBag = (Bag*) GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
	if (!pBag || pBag == pSrcItem)
		return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;

	if (pSrcItem && pSrcItem->IsBag() && !((Bag*) pSrcItem)->IsEmpty())
		return EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS;

	ItemPrototype const* pBagProto = pBag->GetProto();
	if (!pBagProto)
		return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;

	// specialized bag mode or non-specilized
	if (non_specialized
			!= (pBagProto->Class == ITEM_CLASS_CONTAINER
					&& pBagProto->SubClass == ITEM_SUBCLASS_CONTAINER))
		return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;

	if (!ItemCanGoIntoBag(pProto, pBagProto))
		return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;

	for (uint32 j = 0; j < pBag->GetBagSize(); j++) {
		// skip specific slot already processed in first called _CanStoreItem_InSpecificSlot
		if (j == skip_slot)
			continue;

		Item* pItem2 = GetItemByPos(bag, j);

		// ignore move item (this slot will be empty at move)
		if (pItem2 == pSrcItem)
			pItem2 = NULL;

		// if merge skip empty, if !merge skip non-empty
		if ((pItem2 != NULL) != merge)
			continue;

		uint32 need_space = pProto->GetMaxStackSize();

		if (pItem2) {
			// can be merged at least partly
			uint8 res = pItem2->CanBeMergedPartlyWith(pProto);
			if (res != EQUIP_ERR_OK)
				continue;

			// descrease at current stacksize
			need_space -= pItem2->GetCount();
		}

		if (need_space > count)
			need_space = count;

		ItemPosCount newPosition = ItemPosCount((bag << 8) | j, need_space);
		if (!newPosition.isContainedIn(dest)) {
			dest.push_back(newPosition);
			count -= need_space;

			if (count == 0)
				return EQUIP_ERR_OK;
		}
	}
	return EQUIP_ERR_OK;
}

uint8 Player::_CanStoreItem_InInventorySlots(uint8 slot_begin, uint8 slot_end,
		ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count,
		bool merge, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot) const {
	//this is never called for non-bag slots so we can do this
	if (pSrcItem && pSrcItem->IsBag() && !((Bag*) pSrcItem)->IsEmpty())
		return EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS;

	for (uint32 j = slot_begin; j < slot_end; j++) {
		// skip specific slot already processed in first called _CanStoreItem_InSpecificSlot
		if (INVENTORY_SLOT_BAG_0 == skip_bag && j == skip_slot)
			continue;

		Item* pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, j);

		// ignore move item (this slot will be empty at move)
		if (pItem2 == pSrcItem)
			pItem2 = NULL;

		// if merge skip empty, if !merge skip non-empty
		if ((pItem2 != NULL) != merge)
			continue;

		uint32 need_space = pProto->GetMaxStackSize();

		if (pItem2) {
			// can be merged at least partly
			uint8 res = pItem2->CanBeMergedPartlyWith(pProto);
			if (res != EQUIP_ERR_OK)
				continue;

			// descrease at current stacksize
			need_space -= pItem2->GetCount();
		}

		if (need_space > count)
			need_space = count;

		ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j,
				need_space);
		if (!newPosition.isContainedIn(dest)) {
			dest.push_back(newPosition);
			count -= need_space;

			if (count == 0)
				return EQUIP_ERR_OK;
		}
	}
	return EQUIP_ERR_OK;
}

uint8 Player::_CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec &dest,
		uint32 entry, uint32 count, Item *pItem, bool swap,
		uint32* no_space_count) const {
	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
			"STORAGE: CanStoreItem bag = %u, slot = %u, item = %u, count = %u",
			bag, slot, entry, count);

	ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(entry);
	if (!pProto) {
		if (no_space_count)
			*no_space_count = count;
		return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND;
	}

	if (pItem) {
		// item used
		if (pItem->m_lootGenerated) {
			if (no_space_count)
				*no_space_count = count;
			return EQUIP_ERR_ALREADY_LOOTED;
		}

		if (pItem->IsBindedNotWith(this)) {
			if (no_space_count)
				*no_space_count = count;
			return EQUIP_ERR_DONT_OWN_THAT_ITEM;
		}
	}

	// check count of items (skip for auto move for same player from bank)
	uint32 no_similar_count = 0; // can't store this amount similar items
	uint8 res = _CanTakeMoreSimilarItems(entry, count, pItem,
			&no_similar_count);
	if (res != EQUIP_ERR_OK) {
		if (count == no_similar_count) {
			if (no_space_count)
				*no_space_count = no_similar_count;
			return res;
		}
		count -= no_similar_count;
	}

	// in specific slot
	if (bag != NULL_BAG && slot != NULL_SLOT) {
		res = _CanStoreItem_InSpecificSlot(bag, slot, dest, pProto, count, swap,
				pItem);
		if (res != EQUIP_ERR_OK) {
			if (no_space_count)
				*no_space_count = count + no_similar_count;
			return res;
		}

		if (count == 0) {
			if (no_similar_count == 0)
				return EQUIP_ERR_OK;

			if (no_space_count)
				*no_space_count = count + no_similar_count;
			return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
		}
	}

	// not specific slot or have space for partly store only in specific slot

	// in specific bag
	if (bag != NULL_BAG) {
		// search stack in bag for merge to
		if (pProto->Stackable != 1) {
			if (bag == INVENTORY_SLOT_BAG_0) // inventory
			{
				res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,
						CURRENCYTOKEN_SLOT_END, dest, pProto, count, true,
						pItem, bag, slot);
				if (res != EQUIP_ERR_OK) {
					if (no_space_count)
						*no_space_count = count + no_similar_count;
					return res;
				}

				if (count == 0) {
					if (no_similar_count == 0)
						return EQUIP_ERR_OK;

					if (no_space_count)
						*no_space_count = count + no_similar_count;
					return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
				}

				res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,
						INVENTORY_SLOT_ITEM_END, dest, pProto, count, true,
						pItem, bag, slot);
				if (res != EQUIP_ERR_OK) {
					if (no_space_count)
						*no_space_count = count + no_similar_count;
					return res;
				}

				if (count == 0) {
					if (no_similar_count == 0)
						return EQUIP_ERR_OK;

					if (no_space_count)
						*no_space_count = count + no_similar_count;
					return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
				}
			} else // equipped bag
			{
				// we need check 2 time (specialized/non_specialized), use NULL_BAG to prevent skipping bag
				res = _CanStoreItem_InBag(bag, dest, pProto, count, true, false,
						pItem, NULL_BAG, slot);
				if (res != EQUIP_ERR_OK)
					res = _CanStoreItem_InBag(bag, dest, pProto, count, true,
							true, pItem, NULL_BAG, slot);

				if (res != EQUIP_ERR_OK) {
					if (no_space_count)
						*no_space_count = count + no_similar_count;
					return res;
				}

				if (count == 0) {
					if (no_similar_count == 0)
						return EQUIP_ERR_OK;

					if (no_space_count)
						*no_space_count = count + no_similar_count;
					return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
				}
			}
		}

		// search free slot in bag for place to
		if (bag == INVENTORY_SLOT_BAG_0) // inventory
		{
			// search free slot - keyring case
			if (pProto->BagFamily & BAG_FAMILY_MASK_KEYS) {
				uint32 keyringSize = GetMaxKeyringSize();
				res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,
						KEYRING_SLOT_START + keyringSize, dest, pProto, count,
						false, pItem, bag, slot);
				if (res != EQUIP_ERR_OK) {
					if (no_space_count)
						*no_space_count = count + no_similar_count;
					return res;
				}

				if (count == 0) {
					if (no_similar_count == 0)
						return EQUIP_ERR_OK;

					if (no_space_count)
						*no_space_count = count + no_similar_count;
					return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
				}

				res = _CanStoreItem_InInventorySlots(CURRENCYTOKEN_SLOT_START,
						CURRENCYTOKEN_SLOT_END, dest, pProto, count, false,
						pItem, bag, slot);
				if (res != EQUIP_ERR_OK) {
					if (no_space_count)
						*no_space_count = count + no_similar_count;
					return res;
				}

				if (count == 0) {
					if (no_similar_count == 0)
						return EQUIP_ERR_OK;

					if (no_space_count)
						*no_space_count = count + no_similar_count;
					return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
				}
			} else if (pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) {
				res = _CanStoreItem_InInventorySlots(CURRENCYTOKEN_SLOT_START,
						CURRENCYTOKEN_SLOT_END, dest, pProto, count, false,
						pItem, bag, slot);
				if (res != EQUIP_ERR_OK) {
					if (no_space_count)
						*no_space_count = count + no_similar_count;
					return res;
				}

				if (count == 0) {
					if (no_similar_count == 0)
						return EQUIP_ERR_OK;

					if (no_space_count)
						*no_space_count = count + no_similar_count;
					return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
				}
			}

			res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,
					INVENTORY_SLOT_ITEM_END, dest, pProto, count, false, pItem,
					bag, slot);
			if (res != EQUIP_ERR_OK) {
				if (no_space_count)
					*no_space_count = count + no_similar_count;
				return res;
			}

			if (count == 0) {
				if (no_similar_count == 0)
					return EQUIP_ERR_OK;

				if (no_space_count)
					*no_space_count = count + no_similar_count;
				return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
			}
		} else // equipped bag
		{
			res = _CanStoreItem_InBag(bag, dest, pProto, count, false, false,
					pItem, NULL_BAG, slot);
			if (res != EQUIP_ERR_OK)
				res = _CanStoreItem_InBag(bag, dest, pProto, count, false, true,
						pItem, NULL_BAG, slot);

			if (res != EQUIP_ERR_OK) {
				if (no_space_count)
					*no_space_count = count + no_similar_count;
				return res;
			}

			if (count == 0) {
				if (no_similar_count == 0)
					return EQUIP_ERR_OK;

				if (no_space_count)
					*no_space_count = count + no_similar_count;
				return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
			}
		}
	}

	// not specific bag or have space for partly store only in specific bag

	// search stack for merge to
	if (pProto->Stackable != 1) {
		res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,
				CURRENCYTOKEN_SLOT_END, dest, pProto, count, true, pItem, bag,
				slot);
		if (res != EQUIP_ERR_OK) {
			if (no_space_count)
				*no_space_count = count + no_similar_count;
			return res;
		}

		if (count == 0) {
			if (no_similar_count == 0)
				return EQUIP_ERR_OK;

			if (no_space_count)
				*no_space_count = count + no_similar_count;
			return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
		}

		res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,
				INVENTORY_SLOT_ITEM_END, dest, pProto, count, true, pItem, bag,
				slot);
		if (res != EQUIP_ERR_OK) {
			if (no_space_count)
				*no_space_count = count + no_similar_count;
			return res;
		}

		if (count == 0) {
			if (no_similar_count == 0)
				return EQUIP_ERR_OK;

			if (no_space_count)
				*no_space_count = count + no_similar_count;
			return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
		}

		if (pProto->BagFamily) {
			for (uint32 i = INVENTORY_SLOT_BAG_START;
					i < INVENTORY_SLOT_BAG_END; i++) {
				res = _CanStoreItem_InBag(i, dest, pProto, count, true, false,
						pItem, bag, slot);
				if (res != EQUIP_ERR_OK)
					continue;

				if (count == 0) {
					if (no_similar_count == 0)
						return EQUIP_ERR_OK;

					if (no_space_count)
						*no_space_count = count + no_similar_count;
					return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
				}
			}
		}

		for (uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END;
				i++) {
			res = _CanStoreItem_InBag(i, dest, pProto, count, true, true, pItem,
					bag, slot);
			if (res != EQUIP_ERR_OK)
				continue;

			if (count == 0) {
				if (no_similar_count == 0)
					return EQUIP_ERR_OK;

				if (no_space_count)
					*no_space_count = count + no_similar_count;
				return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
			}
		}
	}

	// search free slot - special bag case
	if (pProto->BagFamily) {
		if (pProto->BagFamily & BAG_FAMILY_MASK_KEYS) {
			uint32 keyringSize = GetMaxKeyringSize();
			res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,
					KEYRING_SLOT_START + keyringSize, dest, pProto, count,
					false, pItem, bag, slot);
			if (res != EQUIP_ERR_OK) {
				if (no_space_count)
					*no_space_count = count + no_similar_count;
				return res;
			}

			if (count == 0) {
				if (no_similar_count == 0)
					return EQUIP_ERR_OK;

				if (no_space_count)
					*no_space_count = count + no_similar_count;
				return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
			}
		} else if (pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) {
			res = _CanStoreItem_InInventorySlots(CURRENCYTOKEN_SLOT_START,
					CURRENCYTOKEN_SLOT_END, dest, pProto, count, false, pItem,
					bag, slot);
			if (res != EQUIP_ERR_OK) {
				if (no_space_count)
					*no_space_count = count + no_similar_count;
				return res;
			}

			if (count == 0) {
				if (no_similar_count == 0)
					return EQUIP_ERR_OK;

				if (no_space_count)
					*no_space_count = count + no_similar_count;
				return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
			}
		}

		for (uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END;
				i++) {
			res = _CanStoreItem_InBag(i, dest, pProto, count, false, false,
					pItem, bag, slot);
			if (res != EQUIP_ERR_OK)
				continue;

			if (count == 0) {
				if (no_similar_count == 0)
					return EQUIP_ERR_OK;

				if (no_space_count)
					*no_space_count = count + no_similar_count;
				return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
			}
		}
	}

	if (pItem && pItem->IsBag() && !((Bag*) pItem)->IsEmpty())
		return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG;

	// search free slot
	res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,
			INVENTORY_SLOT_ITEM_END, dest, pProto, count, false, pItem, bag,
			slot);
	if (res != EQUIP_ERR_OK) {
		if (no_space_count)
			*no_space_count = count + no_similar_count;
		return res;
	}

	if (count == 0) {
		if (no_similar_count == 0)
			return EQUIP_ERR_OK;

		if (no_space_count)
			*no_space_count = count + no_similar_count;
		return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
	}

	for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) {
		res = _CanStoreItem_InBag(i, dest, pProto, count, false, true, pItem,
				bag, slot);
		if (res != EQUIP_ERR_OK)
			continue;

		if (count == 0) {
			if (no_similar_count == 0)
				return EQUIP_ERR_OK;

			if (no_space_count)
				*no_space_count = count + no_similar_count;
			return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
		}
	}

	if (no_space_count)
		*no_space_count = count + no_similar_count;

	return EQUIP_ERR_INVENTORY_FULL;
}

void Player::SendCurrencies() const {
	WorldPacket packet(SMSG_INIT_CURRENCY,
			4 + m_currencies.size() * (5 * 4 + 1));
	packet << uint32(m_currencies.size());

	for (PlayerCurrenciesMap::const_iterator itr = m_currencies.begin();
			itr != m_currencies.end(); ++itr) {
		const CurrencyTypesEntry* entry = sCurrencyTypesStore.LookupEntry(
				itr->first);
		packet << uint32(itr->second.weekCount / PLAYER_CURRENCY_PRECISION);
		packet << uint8(0); // unknown
		packet << uint32(entry->ID);
		packet << uint32(sWorld->GetNextCurrencyResetTime() - WEEK);
		packet
				<< uint32(
						_GetCurrencyWeekCap(entry) / PLAYER_CURRENCY_PRECISION);
		packet << uint32(itr->second.totalCount / PLAYER_CURRENCY_PRECISION);
	}

	GetSession()->SendPacket(&packet);
}

uint32 Player::GetCurrency(uint32 id) const {
	PlayerCurrenciesMap::const_iterator itr = m_currencies.find(id);
	return itr != m_currencies.end() ? itr->second.totalCount : 0;
}

bool Player::HasCurrency(uint32 id, uint32 count) const {
	PlayerCurrenciesMap::const_iterator itr = m_currencies.find(id);
	return itr != m_currencies.end() && itr->second.totalCount >= count;
}

void Player::ModifyCurrency(uint32 id, int32 count) {
	if (!count)
		return;

	const CurrencyTypesEntry* currency = sCurrencyTypesStore.LookupEntry(id);
	ASSERT(currency);

	int32 oldTotalCount = 0;
	int32 oldWeekCount = 0;
	PlayerCurrenciesMap::iterator itr = m_currencies.find(id);
	if (itr == m_currencies.end()) {
		PlayerCurrency cur;
		cur.state = PLAYERCURRENCY_NEW;
		cur.totalCount = 0;
		cur.weekCount = 0;
		m_currencies[id] = cur;
		itr = m_currencies.find(id);
	} else {
		oldTotalCount = itr->second.totalCount;
		oldWeekCount = itr->second.weekCount;
	}

	int32 newTotalCount = oldTotalCount + count;
	if (newTotalCount < 0)
		newTotalCount = 0;

	int32 newWeekCount = oldWeekCount + (count > 0 ? count : 0);
	if (newWeekCount < 0)
		newWeekCount = 0;

	uint32 totalCap = _GetCurrencyTotalCap(currency);
	if (totalCap && int32(totalCap) < newTotalCount) {
		int32 delta = newTotalCount - totalCap;
		newTotalCount = totalCap;
		newWeekCount -= delta;
	}

	uint32 weekCap = _GetCurrencyWeekCap(currency);
	if (weekCap && int32(weekCap) < newWeekCount) {
		int32 delta = newWeekCount - weekCap;
		newWeekCount = weekCap;
		newTotalCount -= delta;
	}

	if (newTotalCount != oldTotalCount) {
		if (itr->second.state != PLAYERCURRENCY_NEW)
			itr->second.state = PLAYERCURRENCY_CHANGED;

		itr->second.totalCount = newTotalCount;
		itr->second.weekCount = newWeekCount;

		// probably excessive checks
		if (IsInWorld() && !GetSession()->PlayerLoading()) {
			WorldPacket packet(SMSG_UPDATE_CURRENCY, 12);
			packet << uint32(id);
			packet
					<< uint32(
							weekCap ?
									(newWeekCount / PLAYER_CURRENCY_PRECISION) :
									0);
			packet << uint32(newTotalCount / PLAYER_CURRENCY_PRECISION);
			GetSession()->SendPacket(&packet);
		}
	}
}

void Player::SetCurrency(uint32 id, uint32 count) {
	ModifyCurrency(id, int32(count) - GetCurrency(id));
}

uint32 Player::_GetCurrencyWeekCap(const CurrencyTypesEntry* currency) const {
	uint32 cap = currency->WeekCap;
	switch (currency->ID) {
	case CURRENCY_TYPE_CONQUEST_POINTS:
		cap = uint32(
				m_conquestPointsWeekCap[CP_SOURCE_ARENA]
						* PLAYER_CURRENCY_PRECISION
						* sWorld->getRate(RATE_CONQUEST_POINTS_WEEK_LIMIT));
		break;
	case CURRENCY_TYPE_JUSTICE_POINTS: {
		uint32 justicecap = sWorld->getIntConfig(
				CONFIG_MAX_JUSTICE_POINTS) * PLAYER_CURRENCY_PRECISION;
		if (justicecap > 0)
			cap = justicecap;
		break;
	}
	}

	if (cap != currency->WeekCap && IsInWorld()
			&& !GetSession()->PlayerLoading()) {
		WorldPacket packet(SMSG_UPDATE_CURRENCY_WEEK_LIMIT, 8);
		packet << uint32(cap / PLAYER_CURRENCY_PRECISION);
		packet << uint32(currency->ID);
		GetSession()->SendPacket(&packet);
	}

	return cap;
}

uint32 Player::_GetCurrencyTotalCap(const CurrencyTypesEntry* currency) const {
	uint32 cap = currency->TotalCap;
	switch (currency->ID) {
	case CURRENCY_TYPE_CONQUEST_POINTS:
		cap = sWorld->getIntConfig(
				CONFIG_MAX_CONQUEST_POINTS) * PLAYER_CURRENCY_PRECISION;
		break;
	case CURRENCY_TYPE_HONOR_POINTS:
		cap = sWorld->getIntConfig(
				CONFIG_MAX_HONOR_POINTS) * PLAYER_CURRENCY_PRECISION;
		break;
	case CURRENCY_TYPE_JUSTICE_POINTS:
		cap = sWorld->getIntConfig(
				CONFIG_MAX_JUSTICE_POINTS) * PLAYER_CURRENCY_PRECISION;
		break;
	}

	return cap;
}

//////////////////////////////////////////////////////////////////////////
uint8 Player::CanStoreItems(Item **pItems, int count) const {
	Item *pItem2;

	// fill space table
	int inv_slot_items[INVENTORY_SLOT_ITEM_END - INVENTORY_SLOT_ITEM_START];
	int inv_bags[INVENTORY_SLOT_BAG_END - INVENTORY_SLOT_BAG_START][MAX_BAG_SIZE];
	int inv_keys[KEYRING_SLOT_END - KEYRING_SLOT_START];
	int inv_tokens[CURRENCYTOKEN_SLOT_END - CURRENCYTOKEN_SLOT_START];

	memset(
			inv_slot_items,
			0,
			sizeof(int)
					* (INVENTORY_SLOT_ITEM_END - INVENTORY_SLOT_ITEM_START));
	memset(
			inv_bags,
			0,
			sizeof(int)
					* (INVENTORY_SLOT_BAG_END - INVENTORY_SLOT_BAG_START)*MAX_BAG_SIZE);
	memset(inv_keys, 0, sizeof(int) * (KEYRING_SLOT_END - KEYRING_SLOT_START));
	memset(inv_tokens, 0,
			sizeof(int) * (CURRENCYTOKEN_SLOT_END - CURRENCYTOKEN_SLOT_START));

	for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END;
			i++) {
		pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, i);

		if (pItem2 && !pItem2->IsInTrade()) {
			inv_slot_items[i - INVENTORY_SLOT_ITEM_START] = pItem2->GetCount();
		}
	}

	for (uint8 i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) {
		pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, i);

		if (pItem2 && !pItem2->IsInTrade()) {
			inv_keys[i - KEYRING_SLOT_START] = pItem2->GetCount();
		}
	}

	for (uint8 i = CURRENCYTOKEN_SLOT_START; i < CURRENCYTOKEN_SLOT_END; i++) {
		pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, i);

		if (pItem2 && !pItem2->IsInTrade()) {
			inv_tokens[i - CURRENCYTOKEN_SLOT_START] = pItem2->GetCount();
		}
	}

	for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) {
		if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) {
			for (uint32 j = 0; j < pBag->GetBagSize(); j++) {
				pItem2 = GetItemByPos(i, j);
				if (pItem2 && !pItem2->IsInTrade()) {
					inv_bags[i - INVENTORY_SLOT_BAG_START][j] =
							pItem2->GetCount();
				}
			}
		}
	}

	// check free space for all items
	for (int k = 0; k < count; ++k) {
		Item *pItem = pItems[k];

		// no item
		if (!pItem)
			continue;

		sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
				"STORAGE: CanStoreItems %i. item = %u, count = %u", k + 1,
				pItem->GetEntry(), pItem->GetCount());
		ItemPrototype const *pProto = pItem->GetProto();

		// strange item
		if (!pProto)
			return EQUIP_ERR_ITEM_NOT_FOUND;

		// item used
		if (pItem->m_lootGenerated)
			return EQUIP_ERR_ALREADY_LOOTED;

		// item it 'bind'
		if (pItem->IsBindedNotWith(this))
			return EQUIP_ERR_DONT_OWN_THAT_ITEM;

		Bag *pBag;
		ItemPrototype const *pBagProto;

		// item is 'one item only'
		uint8 res = CanTakeMoreSimilarItems(pItem);
		if (res != EQUIP_ERR_OK)
			return res;

		// search stack for merge to
		if (pProto->Stackable != 1) {
			bool b_found = false;

			for (int t = KEYRING_SLOT_START; t < KEYRING_SLOT_END; ++t) {
				pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, t);
				if (pItem2
						&& pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK
						&& inv_keys[t - KEYRING_SLOT_START] + pItem->GetCount()
								<= pProto->GetMaxStackSize()) {
					inv_keys[t - KEYRING_SLOT_START] += pItem->GetCount();
					b_found = true;
					break;
				}
			}
			if (b_found)
				continue;

			for (int t = CURRENCYTOKEN_SLOT_START; t < CURRENCYTOKEN_SLOT_END;
					++t) {
				pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, t);
				if (pItem2
						&& pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK
						&& inv_tokens[t - CURRENCYTOKEN_SLOT_START]
								+ pItem->GetCount() <= pProto->GetMaxStackSize()) {
					inv_tokens[t - CURRENCYTOKEN_SLOT_START] +=
							pItem->GetCount();
					b_found = true;
					break;
				}
			}
			if (b_found)
				continue;

			for (int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END;
					++t) {
				pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, t);
				if (pItem2
						&& pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK
						&& inv_slot_items[t - INVENTORY_SLOT_ITEM_START]
								+ pItem->GetCount() <= pProto->GetMaxStackSize()) {
					inv_slot_items[t - INVENTORY_SLOT_ITEM_START] +=
							pItem->GetCount();
					b_found = true;
					break;
				}
			}
			if (b_found)
				continue;

			for (int t = INVENTORY_SLOT_BAG_START;
					!b_found && t < INVENTORY_SLOT_BAG_END; ++t) {
				pBag = (Bag*) GetItemByPos(INVENTORY_SLOT_BAG_0, t);
				if (pBag
						&& ItemCanGoIntoBag(pItem->GetProto(), pBag->GetProto())) {
					for (uint32 j = 0; j < pBag->GetBagSize(); j++) {
						pItem2 = GetItemByPos(t, j);
						if (pItem2
								&& pItem2->CanBeMergedPartlyWith(pProto)
										== EQUIP_ERR_OK
								&& inv_bags[t - INVENTORY_SLOT_BAG_START][j]
										+ pItem->GetCount()
										<= pProto->GetMaxStackSize()) {
							inv_bags[t - INVENTORY_SLOT_BAG_START][j] +=
									pItem->GetCount();
							b_found = true;
							break;
						}
					}
				}
			}
			if (b_found)
				continue;
		}

		// special bag case
		if (pProto->BagFamily) {
			bool b_found = false;
			if (pProto->BagFamily & BAG_FAMILY_MASK_KEYS) {
				uint32 keyringSize = GetMaxKeyringSize();
				for (uint32 t = KEYRING_SLOT_START;
						t < KEYRING_SLOT_START + keyringSize; ++t) {
					if (inv_keys[t - KEYRING_SLOT_START] == 0) {
						inv_keys[t - KEYRING_SLOT_START] = 1;
						b_found = true;
						break;
					}
				}
			}

			if (b_found)
				continue;

			if (pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS) {
				for (uint32 t = CURRENCYTOKEN_SLOT_START;
						t < CURRENCYTOKEN_SLOT_END; ++t) {
					if (inv_tokens[t - CURRENCYTOKEN_SLOT_START] == 0) {
						inv_tokens[t - CURRENCYTOKEN_SLOT_START] = 1;
						b_found = true;
						break;
					}
				}
			}

			if (b_found)
				continue;

			for (int t = INVENTORY_SLOT_BAG_START;
					!b_found && t < INVENTORY_SLOT_BAG_END; ++t) {
				pBag = (Bag*) GetItemByPos(INVENTORY_SLOT_BAG_0, t);
				if (pBag) {
					pBagProto = pBag->GetProto();

					// not plain container check
					if (pBagProto
							&& (pBagProto->Class != ITEM_CLASS_CONTAINER
									|| pBagProto->SubClass
											!= ITEM_SUBCLASS_CONTAINER)
							&& ItemCanGoIntoBag(pProto, pBagProto)) {
						for (uint32 j = 0; j < pBag->GetBagSize(); j++) {
							if (inv_bags[t - INVENTORY_SLOT_BAG_START][j]
									== 0) {
								inv_bags[t - INVENTORY_SLOT_BAG_START][j] = 1;
								b_found = true;
								break;
							}
						}
					}
				}
			}
			if (b_found)
				continue;
		}

		// search free slot
		bool b_found = false;
		for (int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END;
				++t) {
			if (inv_slot_items[t - INVENTORY_SLOT_ITEM_START] == 0) {
				inv_slot_items[t - INVENTORY_SLOT_ITEM_START] = 1;
				b_found = true;
				break;
			}
		}
		if (b_found)
			continue;

		// search free slot in bags
		for (int t = INVENTORY_SLOT_BAG_START;
				!b_found && t < INVENTORY_SLOT_BAG_END; ++t) {
			pBag = (Bag*) GetItemByPos(INVENTORY_SLOT_BAG_0, t);
			if (pBag) {
				pBagProto = pBag->GetProto();

				// special bag already checked
				if (pBagProto
						&& (pBagProto->Class != ITEM_CLASS_CONTAINER
								|| pBagProto->SubClass
										!= ITEM_SUBCLASS_CONTAINER))
					continue;

				for (uint32 j = 0; j < pBag->GetBagSize(); j++) {
					if (inv_bags[t - INVENTORY_SLOT_BAG_START][j] == 0) {
						inv_bags[t - INVENTORY_SLOT_BAG_START][j] = 1;
						b_found = true;
						break;
					}
				}
			}
		}

		// no free slot found?
		if (!b_found)
			return EQUIP_ERR_INVENTORY_FULL;
	}

	return EQUIP_ERR_OK;
}

//////////////////////////////////////////////////////////////////////////
uint8 Player::CanEquipNewItem(uint8 slot, uint16 &dest, uint32 item,
		bool swap) const {
	dest = 0;
	Item *pItem = Item::CreateItem(item, 1, this);
	if (pItem) {
		uint8 result = CanEquipItem(slot, dest, pItem, swap);
		delete pItem;
		return result;
	}

	return EQUIP_ERR_ITEM_NOT_FOUND;
}

uint8 Player::CanEquipItem(uint8 slot, uint16 &dest, Item *pItem, bool swap,
		bool not_loading) const {
	dest = 0;
	if (pItem) {
		sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
				"STORAGE: CanEquipItem slot = %u, item = %u, count = %u", slot,
				pItem->GetEntry(), pItem->GetCount());
		ItemPrototype const *pProto = pItem->GetProto();
		if (pProto) {
			// item used
			if (pItem->m_lootGenerated)
				return EQUIP_ERR_ALREADY_LOOTED;

			if (pItem->IsBindedNotWith(this))
				return EQUIP_ERR_DONT_OWN_THAT_ITEM;

			// check count of items (skip for auto move for same player from bank)
			uint8 res = CanTakeMoreSimilarItems(pItem);
			if (res != EQUIP_ERR_OK)
				return res;

			// check this only in game
			if (not_loading) {
				// May be here should be more stronger checks; STUNNED checked
				// ROOT, CONFUSED, DISTRACTED, FLEEING this needs to be checked.
				if (HasUnitState(UNIT_STAT_STUNNED))
					return EQUIP_ERR_YOU_ARE_STUNNED;

				// do not allow equipping gear except weapons, offhands, projectiles, relics in
				// - combat
				// - in-progress arenas
				if (!pProto->CanChangeEquipStateInCombat()) {
					if (isInCombat())
						return EQUIP_ERR_NOT_IN_COMBAT;

					if (Battleground* bg = GetBattleground())
						if (bg->isArena()
								&& bg->GetStatus() == STATUS_IN_PROGRESS)
							return EQUIP_ERR_NOT_DURING_ARENA_MATCH;
				}

				if (isInCombat()
						&& (pProto->Class == ITEM_CLASS_WEAPON
								|| pProto->InventoryType == INVTYPE_RELIC)
						&& m_weaponChangeTimer != 0)
					return EQUIP_ERR_CANT_DO_RIGHT_NOW; // maybe exist better err

				if (IsNonMeleeSpellCasted(false))
					return EQUIP_ERR_CANT_DO_RIGHT_NOW;
			}

			ScalingStatDistributionEntry const *ssd =
					pProto->ScalingStatDistribution ?
							sScalingStatDistributionStore.LookupEntry(
									pProto->ScalingStatDistribution) :
							0;
			// check allowed level (extend range to upper values if MaxLevel more or equal max player level, this let GM set high level with 1...max range items)
			if (ssd && ssd->MaxLevel < DEFAULT_MAX_LEVEL
					&& ssd->MaxLevel < getLevel())
				return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;

			uint8 eslot = FindEquipSlot(pProto, slot, swap);
			if (eslot == NULL_SLOT)
				return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;

			res = CanUseItem(pItem, not_loading);
			if (res != EQUIP_ERR_OK)
				return res;

			if (!swap && GetItemByPos(INVENTORY_SLOT_BAG_0, eslot))
				return EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE;

			// if swap ignore item (equipped also)
			uint8 res2 = 0;
			if (swap)
				res2 = CanEquipUniqueItem(pItem, eslot);
			else
				res2 = CanEquipUniqueItem(pItem, NULL_SLOT);
			if (res2)
				return res2;

			// check unique-equipped special item classes
			if (pProto->Class == ITEM_CLASS_QUIVER) {
				for (uint8 i = INVENTORY_SLOT_BAG_START;
						i < INVENTORY_SLOT_BAG_END; ++i) {
					if (Item* pBag = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) {
						if (pBag != pItem) {
							if (ItemPrototype const* pBagProto = pBag->GetProto()) {
								if (pBagProto->Class == pProto->Class
										&& (!swap || pBag->GetSlot() != eslot))
									return (pBagProto->SubClass
											== ITEM_SUBCLASS_AMMO_POUCH) ?
											EQUIP_ERR_CAN_EQUIP_ONLY1_AMMOPOUCH :
											EQUIP_ERR_CAN_EQUIP_ONLY1_QUIVER;
							}
						}
					}
				}
			}

			uint32 type = pProto->InventoryType;

			if (eslot == EQUIPMENT_SLOT_OFFHAND) {
				if (type == INVTYPE_WEAPON || type == INVTYPE_WEAPONOFFHAND) {
					if (!CanDualWield())
						return EQUIP_ERR_CANT_DUAL_WIELD;
				} else if (type == INVTYPE_2HWEAPON) {
					if (!CanDualWield() || !CanTitanGrip())
						return EQUIP_ERR_CANT_DUAL_WIELD;
				}

				if (IsTwoHandUsed())
					return EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED;
			}

			// equip two-hand weapon case (with possible unequip 2 items)
			if (type == INVTYPE_2HWEAPON) {
				if (eslot == EQUIPMENT_SLOT_OFFHAND) {
					if (!CanTitanGrip())
						return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
				} else if (eslot != EQUIPMENT_SLOT_MAINHAND)
					return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;

				if (!CanTitanGrip()) {
					// offhand item must can be stored in inventory for offhand item and it also must be unequipped
					Item *offItem = GetItemByPos(INVENTORY_SLOT_BAG_0,
							EQUIPMENT_SLOT_OFFHAND);
					ItemPosCountVec off_dest;
					if (offItem
							&& (!not_loading
									|| CanUnequipItem(
											uint16(INVENTORY_SLOT_BAG_0) << 8
													| EQUIPMENT_SLOT_OFFHAND,
											false) != EQUIP_ERR_OK
									|| CanStoreItem(NULL_BAG, NULL_SLOT,
											off_dest, offItem, false)
											!= EQUIP_ERR_OK))
						return swap ?
								EQUIP_ERR_ITEMS_CANT_BE_SWAPPED :
								EQUIP_ERR_INVENTORY_FULL;
				}
			}
			dest = ((INVENTORY_SLOT_BAG_0 << 8) | eslot);
			return EQUIP_ERR_OK;
		}
	}

	return !swap ? EQUIP_ERR_ITEM_NOT_FOUND : EQUIP_ERR_ITEMS_CANT_BE_SWAPPED;
}

uint8 Player::CanUnequipItem(uint16 pos, bool swap) const {
	// Applied only to equipped items and bank bags
	if (!IsEquipmentPos(pos) && !IsBagPos(pos))
		return EQUIP_ERR_OK;

	Item* pItem = GetItemByPos(pos);

	// Applied only to existed equipped item
	if (!pItem)
		return EQUIP_ERR_OK;

	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
			"STORAGE: CanUnequipItem slot = %u, item = %u, count = %u", pos,
			pItem->GetEntry(), pItem->GetCount());

	ItemPrototype const *pProto = pItem->GetProto();
	if (!pProto)
		return EQUIP_ERR_ITEM_NOT_FOUND;

	// item used
	if (pItem->m_lootGenerated)
		return EQUIP_ERR_ALREADY_LOOTED;

	// do not allow unequipping gear except weapons, offhands, projectiles, relics in
	// - combat
	// - in-progress arenas
	if (!pProto->CanChangeEquipStateInCombat()) {
		if (isInCombat())
			return EQUIP_ERR_NOT_IN_COMBAT;

		if (Battleground* bg = GetBattleground())
			if (bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS)
				return EQUIP_ERR_NOT_DURING_ARENA_MATCH;
	}

	if (!swap && pItem->IsBag() && !((Bag*) pItem)->IsEmpty())
		return EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS;

	return EQUIP_ERR_OK;
}

uint8 Player::CanBankItem(uint8 bag, uint8 slot, ItemPosCountVec &dest,
		Item *pItem, bool swap, bool not_loading) const {
	if (!pItem)
		return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND;

	uint32 count = pItem->GetCount();

	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
			"STORAGE: CanBankItem bag = %u, slot = %u, item = %u, count = %u",
			bag, slot, pItem->GetEntry(), pItem->GetCount());
	ItemPrototype const *pProto = pItem->GetProto();
	if (!pProto)
		return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND;

	// item used
	if (pItem->m_lootGenerated)
		return EQUIP_ERR_ALREADY_LOOTED;

	if (pItem->IsBindedNotWith(this))
		return EQUIP_ERR_DONT_OWN_THAT_ITEM;

	// Currency tokens are not supposed to be swapped out of their hidden bag
	uint8 pItemslot = pItem->GetSlot();
	if (pItemslot >= CURRENCYTOKEN_SLOT_START
			&& pItemslot < CURRENCYTOKEN_SLOT_END) {
		sLog->outError(
				"Possible hacking attempt: Player %s [guid: %u] tried to move token [guid: %u, entry: %u] out of the currency bag!",
				GetName(), GetGUIDLow(), pItem->GetGUIDLow(), pProto->ItemId);
		return EQUIP_ERR_ITEMS_CANT_BE_SWAPPED;
	}

	// check count of items (skip for auto move for same player from bank)
	uint8 res = CanTakeMoreSimilarItems(pItem);
	if (res != EQUIP_ERR_OK)
		return res;

	// in specific slot
	if (bag != NULL_BAG && slot != NULL_SLOT) {
		if (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END) {
			if (!pItem->IsBag())
				return EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT;

			if (slot - BANK_SLOT_BAG_START >= GetBankBagSlotCount())
				return EQUIP_ERR_MUST_PURCHASE_THAT_BAG_SLOT;

			res = CanUseItem(pItem, not_loading);
			if (res != EQUIP_ERR_OK)
				return res;
		}

		res = _CanStoreItem_InSpecificSlot(bag, slot, dest, pProto, count, swap,
				pItem);
		if (res != EQUIP_ERR_OK)
			return res;

		if (count == 0)
			return EQUIP_ERR_OK;
	}

	// not specific slot or have space for partly store only in specific slot

	// in specific bag
	if (bag != NULL_BAG) {
		if (pProto->InventoryType == INVTYPE_BAG) {
			Bag *pBag = (Bag*) pItem;
			if (pBag && !pBag->IsEmpty())
				return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG;
		}

		// search stack in bag for merge to
		if (pProto->Stackable != 1) {
			if (bag == INVENTORY_SLOT_BAG_0) {
				res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,
						BANK_SLOT_ITEM_END, dest, pProto, count, true, pItem,
						bag, slot);
				if (res != EQUIP_ERR_OK)
					return res;

				if (count == 0)
					return EQUIP_ERR_OK;
			} else {
				res = _CanStoreItem_InBag(bag, dest, pProto, count, true, false,
						pItem, NULL_BAG, slot);
				if (res != EQUIP_ERR_OK)
					res = _CanStoreItem_InBag(bag, dest, pProto, count, true,
							true, pItem, NULL_BAG, slot);

				if (res != EQUIP_ERR_OK)
					return res;

				if (count == 0)
					return EQUIP_ERR_OK;
			}
		}

		// search free slot in bag
		if (bag == INVENTORY_SLOT_BAG_0) {
			res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,
					BANK_SLOT_ITEM_END, dest, pProto, count, false, pItem, bag,
					slot);
			if (res != EQUIP_ERR_OK)
				return res;

			if (count == 0)
				return EQUIP_ERR_OK;
		} else {
			res = _CanStoreItem_InBag(bag, dest, pProto, count, false, false,
					pItem, NULL_BAG, slot);
			if (res != EQUIP_ERR_OK)
				res = _CanStoreItem_InBag(bag, dest, pProto, count, false, true,
						pItem, NULL_BAG, slot);

			if (res != EQUIP_ERR_OK)
				return res;

			if (count == 0)
				return EQUIP_ERR_OK;
		}
	}

	// not specific bag or have space for partly store only in specific bag

	// search stack for merge to
	if (pProto->Stackable != 1) {
		// in slots
		res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,
				BANK_SLOT_ITEM_END, dest, pProto, count, true, pItem, bag,
				slot);
		if (res != EQUIP_ERR_OK)
			return res;

		if (count == 0)
			return EQUIP_ERR_OK;

		// in special bags
		if (pProto->BagFamily) {
			for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++) {
				res = _CanStoreItem_InBag(i, dest, pProto, count, true, false,
						pItem, bag, slot);
				if (res != EQUIP_ERR_OK)
					continue;

				if (count == 0)
					return EQUIP_ERR_OK;
			}
		}

		for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++) {
			res = _CanStoreItem_InBag(i, dest, pProto, count, true, true, pItem,
					bag, slot);
			if (res != EQUIP_ERR_OK)
				continue;

			if (count == 0)
				return EQUIP_ERR_OK;
		}
	}

	// search free place in special bag
	if (pProto->BagFamily) {
		for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++) {
			res = _CanStoreItem_InBag(i, dest, pProto, count, false, false,
					pItem, bag, slot);
			if (res != EQUIP_ERR_OK)
				continue;

			if (count == 0)
				return EQUIP_ERR_OK;
		}
	}

	// search free space
	res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,
			BANK_SLOT_ITEM_END, dest, pProto, count, false, pItem, bag, slot);
	if (res != EQUIP_ERR_OK)
		return res;

	if (count == 0)
		return EQUIP_ERR_OK;

	for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++) {
		res = _CanStoreItem_InBag(i, dest, pProto, count, false, true, pItem,
				bag, slot);
		if (res != EQUIP_ERR_OK)
			continue;

		if (count == 0)
			return EQUIP_ERR_OK;
	}
	return EQUIP_ERR_BANK_FULL;
}

uint8 Player::CanUseItem(Item *pItem, bool not_loading) const {
	if (pItem) {
		sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "STORAGE: CanUseItem item = %u",
				pItem->GetEntry());

		if (!isAlive() && not_loading)
			return EQUIP_ERR_YOU_ARE_DEAD;

		//if (isStunned())
		//    return EQUIP_ERR_YOU_ARE_STUNNED;

		ItemPrototype const *pProto = pItem->GetProto();
		if (pProto) {
			if (pItem->IsBindedNotWith(this))
				return EQUIP_ERR_DONT_OWN_THAT_ITEM;

			uint8 res = CanUseItem(pProto);
			if (res != EQUIP_ERR_OK)
				return res;

			if (pItem->GetSkill() != 0) {
				bool allowEquip = false;
				uint32 itemSkill = pItem->GetSkill();
				// Armor that is binded to account can "morph" from plate to mail, etc. if skill is not learned yet.
				if (pProto->Quality == ITEM_QUALITY_HEIRLOOM
						&& pProto->Class == ITEM_CLASS_ARMOR
						&& !HasSkill(itemSkill)) {
					// TODO: when you right-click already equipped item it throws EQUIP_ERR_NO_REQUIRED_PROFICIENCY.

					// In fact it's a visual bug, everything works properly... I need sniffs of operations with
					// binded to account items from off server.

					switch (getClass()) {
					case CLASS_HUNTER:
					case CLASS_SHAMAN:
						allowEquip = (itemSkill == SKILL_MAIL);
						break;
					case CLASS_PALADIN:
					case CLASS_WARRIOR:
						allowEquip = (itemSkill == SKILL_PLATE_MAIL);
						break;
					}
				}
				if (!allowEquip && GetSkillValue(itemSkill) == 0)
					return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
			}

			if (pProto->RequiredReputationFaction
					&& uint32(
							GetReputationRank(
									pProto->RequiredReputationFaction))
							< pProto->RequiredReputationRank)
				return EQUIP_ERR_CANT_EQUIP_REPUTATION;

			return EQUIP_ERR_OK;
		}
	}
	return EQUIP_ERR_ITEM_NOT_FOUND;
}

uint8 Player::CanUseItem(ItemPrototype const *proto) const {
    // Used by group, function NeedBeforeGreed, to know if a prototype can be used by a player

    if (proto)
    {
        if ((proto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY) && GetTeam() != HORDE)
            return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;

        if ((proto->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) && GetTeam() != ALLIANCE)
            return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;

        if ((proto->AllowableClass & getClassMask()) == 0 || (proto->AllowableRace & getRaceMask()) == 0)
            return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;

        if (proto->RequiredSkill != 0)
        {
            if (GetSkillValue(proto->RequiredSkill) == 0)
                return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
            else if (GetSkillValue(proto->RequiredSkill) < proto->RequiredSkillRank)
                return EQUIP_ERR_CANT_EQUIP_SKILL;
        }

        if (proto->RequiredSpell != 0 && !HasSpell(proto->RequiredSpell))
            return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;

        if (getLevel() < proto->RequiredLevel)
            return EQUIP_ERR_CANT_EQUIP_LEVEL_I;

        // If World Event is not active, prevent using event dependent items
        if (proto->HolidayId && !IsHolidayActive((HolidayIds)proto->HolidayId))
            return EQUIP_ERR_CANT_DO_RIGHT_NOW;

        return EQUIP_ERR_OK;
    }

    return EQUIP_ERR_ITEM_NOT_FOUND;
}

uint8 Player::CanUseAmmo(uint32 item) const {
	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "STORAGE: CanUseAmmo item = %u",
			item);
	if (!isAlive())
		return EQUIP_ERR_YOU_ARE_DEAD;
	//if (isStunned())
	//    return EQUIP_ERR_YOU_ARE_STUNNED;
	ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(item);
	if (pProto) {
		if (pProto->InventoryType != INVTYPE_AMMO)
			return EQUIP_ERR_ONLY_AMMO_CAN_GO_HERE;

		uint8 res = CanUseItem(pProto);
		if (res != EQUIP_ERR_OK)
			return res;

		/*if (GetReputationMgr().GetReputation() < pProto->RequiredReputation)
		 return EQUIP_ERR_CANT_EQUIP_REPUTATION;
		 */

		// Requires No Ammo
		if (HasAura(46699))
			return EQUIP_ERR_BAG_FULL6;

		return EQUIP_ERR_OK;
	}
	return EQUIP_ERR_ITEM_NOT_FOUND;
}

void Player::SetAmmo(uint32 item) {
	//if (!item)
	//    return;

	//// already set
	//if (GetUInt32Value(PLAYER_AMMO_ID) == item)
	//    return;

	//// check ammo
	//if (item)
	//{
	//    uint8 msg = CanUseAmmo(item);
	//    if (msg != EQUIP_ERR_OK)
	//    {
	//        SendEquipError(msg, NULL, NULL, item);
	//        return;
	//    }
	//}

	//SetUInt32Value(PLAYER_AMMO_ID, item);

	//_ApplyAmmoBonuses();
}

void Player::RemoveAmmo() {
	//SetUInt32Value(PLAYER_AMMO_ID, 0);

	//m_ammoDPS = 0.0f;

	//if (CanModifyStats())
	//    UpdateDamagePhysical(RANGED_ATTACK);
}

// Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
Item* Player::StoreNewItem(ItemPosCountVec const& dest, uint32 item,
		bool update, int32 randomPropertyId, AllowedLooterSet* allowedLooters) {
	uint32 count = 0;
	for (ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end();
			++itr)
		count += itr->count;

	Item *pItem = Item::CreateItem(item, count, this);
	if (pItem) {
		ItemAddedQuestCheck(item, count);
		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, item, count);
		if (randomPropertyId)
			pItem->SetItemRandomProperties(randomPropertyId);
		pItem = StoreItem(dest, pItem, update);

		const ItemPrototype *proto = pItem->GetProto();
		for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
			if (proto->Spells[i].SpellTrigger
					== ITEM_SPELLTRIGGER_ON_NO_DELAY_USE
					&& proto->Spells[i].SpellId > 0) // On obtain trigger
				CastSpell(this, proto->Spells[i].SpellId, true, pItem);

		if (allowedLooters && pItem->GetProto()->GetMaxStackSize() == 1
				&& pItem->IsSoulBound()) {
			pItem->SetSoulboundTradeable(allowedLooters, this, true);
			pItem->SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME,
					GetTotalPlayedTime());
			m_itemSoulboundTradeable.push_back(pItem);

			// save data
			std::ostringstream ss;
			for (AllowedLooterSet::iterator itr = allowedLooters->begin();
					itr != allowedLooters->end(); ++itr)
				ss << *itr << " ";

			PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(
					CHAR_ADD_ITEM_BOP_TRADE);
			stmt->setUInt32(0, pItem->GetGUIDLow());
			stmt->setString(1, ss.str());
			CharacterDatabase.Execute(stmt);
		}
	}
	return pItem;
}

Item* Player::StoreItem(ItemPosCountVec const& dest, Item* pItem, bool update) {
	if (!pItem)
		return NULL;

	Item* lastItem = pItem;
	uint32 entry = pItem->GetEntry();
	for (ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end();
			) {
		uint16 pos = itr->pos;
		uint32 count = itr->count;

		++itr;

		if (itr == dest.end()) {
			lastItem = _StoreItem(pos, pItem, count, false, update);
			break;
		}

		lastItem = _StoreItem(pos, pItem, count, true, update);
	}
	GetAchievementMgr().UpdateAchievementCriteria(
			ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM, entry);
	return lastItem;
}

// Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
Item* Player::_StoreItem(uint16 pos, Item *pItem, uint32 count, bool clone,
		bool update) {
	if (!pItem)
		return NULL;

	uint8 bag = pos >> 8;
	uint8 slot = pos & 255;

	sLog->outDebug(
			LOG_FILTER_PLAYER_ITEMS,
			"STORAGE: StoreItem bag = %u, slot = %u, item = %u, count = %u, guid = %u",
			bag, slot, pItem->GetEntry(), count, pItem->GetGUIDLow());

	Item *pItem2 = GetItemByPos(bag, slot);

	if (!pItem2) {
		if (clone)
			pItem = pItem->CloneItem(count, this);
		else
			pItem->SetCount(count);

		if (!pItem)
			return NULL;

		if (pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP
				|| pItem->GetProto()->Bonding == BIND_QUEST_ITEM
				|| (pItem->GetProto()->Bonding == BIND_WHEN_EQUIPED
						&& IsBagPos(pos)))
			pItem->SetBinding(true);

		if (bag == INVENTORY_SLOT_BAG_0) {
			m_items[slot] = pItem;
			SetUInt64Value(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2),
					pItem->GetGUID());
			pItem->SetUInt64Value(ITEM_FIELD_CONTAINED, GetGUID());
			pItem->SetUInt64Value(ITEM_FIELD_OWNER, GetGUID());

			pItem->SetSlot(slot);
			pItem->SetContainer(NULL);

			if (IsInWorld() && update) {
				pItem->AddToWorld();
				pItem->SendUpdateToPlayer(this);
			}

			pItem->SetState(ITEM_CHANGED, this);
		} else if (Bag *pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, bag)) {
			pBag->StoreItem(slot, pItem, update);
			if (IsInWorld() && update) {
				pItem->AddToWorld();
				pItem->SendUpdateToPlayer(this);
			}
			pItem->SetState(ITEM_CHANGED, this);
			pBag->SetState(ITEM_CHANGED, this);
		}

		AddEnchantmentDurations(pItem);
		AddItemDurations(pItem);

		return pItem;
	} else {
		if (pItem2->GetProto()->Bonding == BIND_WHEN_PICKED_UP
				|| pItem2->GetProto()->Bonding == BIND_QUEST_ITEM
				|| (pItem2->GetProto()->Bonding == BIND_WHEN_EQUIPED
						&& IsBagPos(pos)))
			pItem2->SetBinding(true);

		pItem2->SetCount(pItem2->GetCount() + count);
		if (IsInWorld() && update)
			pItem2->SendUpdateToPlayer(this);

		if (!clone) {
			// delete item (it not in any slot currently)
			if (IsInWorld() && update) {
				pItem->RemoveFromWorld();
				pItem->DestroyForPlayer(this);
			}

			RemoveEnchantmentDurations(pItem);
			RemoveItemDurations(pItem);

			pItem->SetOwnerGUID(GetGUID()); // prevent error at next SetState in case trade/mail/buy from vendor
			pItem->SetNotRefundable(this);
			pItem->SetSoulboundTradeable(NULL, this, false);
			RemoveTradeableItem(pItem);
			pItem->SetState(ITEM_REMOVED, this);
		}

		AddEnchantmentDurations(pItem2);

		pItem2->SetState(ITEM_CHANGED, this);

		return pItem2;
	}
}

Item* Player::EquipNewItem(uint16 pos, uint32 item, bool update) {
	if (Item *pItem = Item::CreateItem(item, 1, this)) {
		ItemAddedQuestCheck(item, 1);
		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, item, 1);
		return EquipItem(pos, pItem, update);
	}

	return NULL;
}

Item* Player::EquipItem(uint16 pos, Item *pItem, bool update) {
	AddEnchantmentDurations(pItem);
	AddItemDurations(pItem);

	uint8 bag = pos >> 8;
	uint8 slot = pos & 255;

	Item *pItem2 = GetItemByPos(bag, slot);

	if (!pItem2) {
		VisualizeItem(slot, pItem);

		if (isAlive()) {
			ItemPrototype const *pProto = pItem->GetProto();

			// item set bonuses applied only at equip and removed at unequip, and still active for broken items
			if (pProto && pProto->ItemSet)
				AddItemsSetItem(this, pItem);

			_ApplyItemMods(pItem, slot, true);

			if (pProto && isInCombat()
					&& (pProto->Class == ITEM_CLASS_WEAPON
							|| pProto->InventoryType == INVTYPE_RELIC)
					&& m_weaponChangeTimer == 0) {
				uint32 cooldownSpell = 6119;

				if (getClass() == CLASS_ROGUE)
					cooldownSpell = 6123;

				SpellEntry const* spellProto = sSpellStore.LookupEntry(
						cooldownSpell);

				if (!spellProto)
					sLog->outError(
							"Weapon switch cooldown spell %u couldn't be found in Spell.dbc",
							cooldownSpell);
				else {
					m_weaponChangeTimer = spellProto->StartRecoveryTime;

					AddGlobalCooldown(spellProto, NULL); // NULL spell is safe (needed for serverside GCD

					CastSpell(this, cooldownSpell, false); // Needed for server side GCD

					WorldPacket data(SMSG_SPELL_COOLDOWN, 8 + 1 + 4);
					data << uint64(GetGUID());
					data << uint8(1);
					data << uint32(cooldownSpell);
					data << uint32(0);
					GetSession()->SendPacket(&data);
				}
			}
		}

		if (IsInWorld() && update) {
			pItem->AddToWorld();
			pItem->SendUpdateToPlayer(this);
		}

		ApplyEquipCooldown(pItem);

		// update expertise and armor penetration - passive auras may need it

		if (slot == EQUIPMENT_SLOT_MAINHAND)
			UpdateExpertise(BASE_ATTACK);

		else if (slot == EQUIPMENT_SLOT_OFFHAND)
			UpdateExpertise(OFF_ATTACK);

		switch (slot) {
		case EQUIPMENT_SLOT_MAINHAND:
		case EQUIPMENT_SLOT_OFFHAND:
		case EQUIPMENT_SLOT_RANGED:
			RecalculateRating(CR_ARMOR_PENETRATION);
		default:
			break;
		}
	} else {
		pItem2->SetCount(pItem2->GetCount() + pItem->GetCount());
		if (IsInWorld() && update)
			pItem2->SendUpdateToPlayer(this);

		// delete item (it not in any slot currently)
		//pItem->DeleteFromDB();
		if (IsInWorld() && update) {
			pItem->RemoveFromWorld();
			pItem->DestroyForPlayer(this);
		}

		RemoveEnchantmentDurations(pItem);
		RemoveItemDurations(pItem);

		pItem->SetOwnerGUID(GetGUID()); // prevent error at next SetState in case trade/mail/buy from vendor
		pItem->SetNotRefundable(this);
		pItem->SetSoulboundTradeable(NULL, this, false);
		RemoveTradeableItem(pItem);
		pItem->SetState(ITEM_REMOVED, this);
		pItem2->SetState(ITEM_CHANGED, this);

		ApplyEquipCooldown(pItem2);

		return pItem2;
	}

	// only for full equip instead adding to stack
	GetAchievementMgr().UpdateAchievementCriteria(
			ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM, pItem->GetEntry());
	GetAchievementMgr().UpdateAchievementCriteria(
			ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM, pItem->GetEntry(), slot);

	return pItem;
}

void Player::QuickEquipItem(uint16 pos, Item *pItem) {
	if (pItem) {
		AddEnchantmentDurations(pItem);
		AddItemDurations(pItem);

		uint8 slot = pos & 255;
		VisualizeItem(slot, pItem);

		if (IsInWorld()) {
			pItem->AddToWorld();
			pItem->SendUpdateToPlayer(this);
		}

		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM, pItem->GetEntry());
		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM, pItem->GetEntry(),
				slot);
	}
}

void Player::SetVisibleItemSlot(uint8 slot, Item *pItem) {
	if (pItem) {
		SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2),
				pItem->GetEntry());
		SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 0,
				pItem->GetEnchantmentId(PERM_ENCHANTMENT_SLOT));
		SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 1,
				pItem->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT));
	} else {
		SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2), 0);
		SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 0);
	}
}

void Player::VisualizeItem(uint8 slot, Item *pItem) {
	if (!pItem)
		return;

	// check also  BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
	if (pItem->GetProto()->Bonding == BIND_WHEN_EQUIPED
			|| pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP
			|| pItem->GetProto()->Bonding == BIND_QUEST_ITEM)
		pItem->SetBinding(true);

	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
			"STORAGE: EquipItem slot = %u, item = %u", slot, pItem->GetEntry());

	m_items[slot] = pItem;
	SetUInt64Value(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), pItem->GetGUID());
	pItem->SetUInt64Value(ITEM_FIELD_CONTAINED, GetGUID());
	pItem->SetUInt64Value(ITEM_FIELD_OWNER, GetGUID());
	pItem->SetSlot(slot);
	pItem->SetContainer(NULL);

	if (slot < EQUIPMENT_SLOT_END)
		SetVisibleItemSlot(slot, pItem);

	pItem->SetState(ITEM_CHANGED, this);
}

void Player::RemoveItem(uint8 bag, uint8 slot, bool update) {
	// note: removeitem does not actually change the item
	// it only takes the item out of storage temporarily
	// note2: if removeitem is to be used for delinking
	// the item must be removed from the player's updatequeue

	Item *pItem = GetItemByPos(bag, slot);
	if (pItem) {
		sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
				"STORAGE: RemoveItem bag = %u, slot = %u, item = %u", bag, slot,
				pItem->GetEntry());

		RemoveEnchantmentDurations(pItem);
		RemoveItemDurations(pItem);
		RemoveTradeableItem(pItem);

		if (bag == INVENTORY_SLOT_BAG_0) {
			if (slot < INVENTORY_SLOT_BAG_END) {
				ItemPrototype const *pProto = pItem->GetProto();
				// item set bonuses applied only at equip and removed at unequip, and still active for broken items

				if (pProto && pProto->ItemSet)
					RemoveItemsSetItem(this, pProto);

				_ApplyItemMods(pItem, slot, false);

				// remove item dependent auras and casts (only weapon and armor slots)
				if (slot < EQUIPMENT_SLOT_END) {
					RemoveItemDependentAurasAndCasts(pItem);

					// remove held enchantments, update expertise
					if (slot == EQUIPMENT_SLOT_MAINHAND) {
						if (pItem->GetItemSuffixFactor()) {
							pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_3);
							pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_4);
						} else {
							pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_0);
							pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_1);
						}

						UpdateExpertise(BASE_ATTACK);
					} else if (slot == EQUIPMENT_SLOT_OFFHAND)
						UpdateExpertise(OFF_ATTACK);
					// update armor penetration - passive auras may need it
					switch (slot) {
					case EQUIPMENT_SLOT_MAINHAND:
					case EQUIPMENT_SLOT_OFFHAND:
					case EQUIPMENT_SLOT_RANGED:
						RecalculateRating(CR_ARMOR_PENETRATION);
					default:
						break;
					}
				}
			}

			m_items[slot] = NULL;
			SetUInt64Value(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), 0);

			if (slot < EQUIPMENT_SLOT_END)
				SetVisibleItemSlot(slot, NULL);
		} else {
			if (Bag *pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, bag))
				pBag->RemoveItem(slot, update);
		}
		pItem->SetUInt64Value(ITEM_FIELD_CONTAINED, 0);
		// pItem->SetUInt64Value(ITEM_FIELD_OWNER, 0); not clear owner at remove (it will be set at store). This used in mail and auction code
		pItem->SetSlot(NULL_SLOT);
		if (IsInWorld() && update)
			pItem->SendUpdateToPlayer(this);
	}
}

// Common operation need to remove item from inventory without delete in trade, auction, guild bank, mail....
void Player::MoveItemFromInventory(uint8 bag, uint8 slot, bool update) {
	if (Item* it = GetItemByPos(bag, slot)) {
		ItemRemovedQuestCheck(it->GetEntry(), it->GetCount());
		RemoveItem(bag, slot, update);
		it->SetNotRefundable(this, false);
		it->RemoveFromUpdateQueueOf(this);
		if (it->IsInWorld()) {
			it->RemoveFromWorld();
			it->DestroyForPlayer(this);
		}
	}
}

// Common operation need to add item from inventory without delete in trade, guild bank, mail....
void Player::MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem,
		bool update, bool in_characterInventoryDB) {
	// update quest counters
	ItemAddedQuestCheck(pItem->GetEntry(), pItem->GetCount());
	GetAchievementMgr().UpdateAchievementCriteria(
			ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, pItem->GetEntry(),
			pItem->GetCount());

	// store item
	Item* pLastItem = StoreItem(dest, pItem, update);

	// only set if not merged to existed stack (pItem can be deleted already but we can compare pointers any way)
	if (pLastItem == pItem) {
		// update owner for last item (this can be original item with wrong owner
		if (pLastItem->GetOwnerGUID() != GetGUID())
			pLastItem->SetOwnerGUID(GetGUID());

		// if this original item then it need create record in inventory
		// in case trade we already have item in other player inventory
		pLastItem->SetState(in_characterInventoryDB ? ITEM_CHANGED : ITEM_NEW,
				this);
	}

	if (pLastItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE))
		m_itemSoulboundTradeable.push_back(pLastItem);
}

void Player::DestroyItem(uint8 bag, uint8 slot, bool update) {
	Item *pItem = GetItemByPos(bag, slot);
	if (pItem) {
		sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
				"STORAGE: DestroyItem bag = %u, slot = %u, item = %u", bag,
				slot, pItem->GetEntry());
		// Also remove all contained items if the item is a bag.
		// This if () prevents item saving crashes if the condition for a bag to be empty before being destroyed was bypassed somehow.
		if (pItem->IsBag() && !((Bag*) pItem)->IsEmpty()) {
			for (uint8 i = 0; i < MAX_BAG_SIZE; ++i)
				DestroyItem(slot, i, update);
		}

		if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED))
			CharacterDatabase.PExecute(
					"DELETE FROM character_gifts WHERE item_guid = '%u'",
					pItem->GetGUIDLow());

		RemoveEnchantmentDurations(pItem);
		RemoveItemDurations(pItem);

		pItem->SetNotRefundable(this);
		pItem->SetSoulboundTradeable(NULL, this, false);
		RemoveTradeableItem(pItem);

		const ItemPrototype *proto = pItem->GetProto();
		for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
			if (proto->Spells[i].SpellTrigger
					== ITEM_SPELLTRIGGER_ON_NO_DELAY_USE
					&& proto->Spells[i].SpellId > 0) // On obtain trigger
				RemoveAurasDueToSpell(proto->Spells[i].SpellId);

		ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount());

		if (bag == INVENTORY_SLOT_BAG_0) {
			SetUInt64Value(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), 0);

			// equipment and equipped bags can have applied bonuses
			if (slot < INVENTORY_SLOT_BAG_END) {
				ItemPrototype const *pProto = pItem->GetProto();

				// item set bonuses applied only at equip and removed at unequip, and still active for broken items
				if (pProto && pProto->ItemSet)
					RemoveItemsSetItem(this, pProto);

				_ApplyItemMods(pItem, slot, false);
			}

			if (slot < EQUIPMENT_SLOT_END) {
				// remove item dependent auras and casts (only weapon and armor slots)
				RemoveItemDependentAurasAndCasts(pItem);

				// update expertise and armor penetration - passive auras may need it
				switch (slot) {
				case EQUIPMENT_SLOT_MAINHAND:
				case EQUIPMENT_SLOT_OFFHAND:
				case EQUIPMENT_SLOT_RANGED:
					RecalculateRating(CR_ARMOR_PENETRATION);
				default:
					break;
				}

				if (slot == EQUIPMENT_SLOT_MAINHAND)
					UpdateExpertise(BASE_ATTACK);
				else if (slot == EQUIPMENT_SLOT_OFFHAND)
					UpdateExpertise(OFF_ATTACK);

				// equipment visual show
				SetVisibleItemSlot(slot, NULL);
			}

			m_items[slot] = NULL;
		} else if (Bag *pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, bag))
			pBag->RemoveItem(slot, update);

		if (IsInWorld() && update) {
			pItem->RemoveFromWorld();
			pItem->DestroyForPlayer(this);
		}

		//pItem->SetOwnerGUID(0);
		pItem->SetUInt64Value(ITEM_FIELD_CONTAINED, 0);
		pItem->SetSlot(NULL_SLOT);
		pItem->SetState(ITEM_REMOVED, this);
	}
}

void Player::DestroyItemCount(uint32 item, uint32 count, bool update,
		bool unequip_check) {
	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
			"STORAGE: DestroyItemCount item = %u, count = %u", item, count);
	uint32 remcount = 0;

	// in inventory
	for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END;
			++i) {
		if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) {
			if (pItem->GetEntry() == item && !pItem->IsInTrade()) {
				if (pItem->GetCount() + remcount <= count) {
					// all items in inventory can unequipped
					remcount += pItem->GetCount();
					DestroyItem(INVENTORY_SLOT_BAG_0, i, update);

					if (remcount >= count)
						return;
				} else {
					ItemRemovedQuestCheck(pItem->GetEntry(), count - remcount);
					pItem->SetCount(pItem->GetCount() - count + remcount);
					if (IsInWorld() & update)
						pItem->SendUpdateToPlayer(this);
					pItem->SetState(ITEM_CHANGED, this);
					return;
				}
			}
		}
	}

	for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i) {
		if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) {
			if (pItem->GetEntry() == item && !pItem->IsInTrade()) {
				if (pItem->GetCount() + remcount <= count) {
					// all keys can be unequipped
					remcount += pItem->GetCount();
					DestroyItem(INVENTORY_SLOT_BAG_0, i, update);

					if (remcount >= count)
						return;
				} else {
					ItemRemovedQuestCheck(pItem->GetEntry(), count - remcount);
					pItem->SetCount(pItem->GetCount() - count + remcount);
					if (IsInWorld() & update)
						pItem->SendUpdateToPlayer(this);
					pItem->SetState(ITEM_CHANGED, this);
					return;
				}
			}
		}
	}

	// in inventory bags
	for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) {
		if (Bag *pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) {
			for (uint32 j = 0; j < pBag->GetBagSize(); j++) {
				if (Item* pItem = pBag->GetItemByPos(j)) {
					if (pItem->GetEntry() == item && !pItem->IsInTrade()) {
						// all items in bags can be unequipped
						if (pItem->GetCount() + remcount <= count) {
							remcount += pItem->GetCount();
							DestroyItem(i, j, update);

							if (remcount >= count)
								return;
						} else {
							ItemRemovedQuestCheck(pItem->GetEntry(),
									count - remcount);
							pItem->SetCount(
									pItem->GetCount() - count + remcount);
							if (IsInWorld() && update)
								pItem->SendUpdateToPlayer(this);
							pItem->SetState(ITEM_CHANGED, this);
							return;
						}
					}
				}
			}
		}
	}

	// in equipment and bag list
	for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++) {
		if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) {
			if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) {
				if (pItem->GetCount() + remcount <= count) {
					if (!unequip_check
							|| CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i,
									false) == EQUIP_ERR_OK) {
						remcount += pItem->GetCount();
						DestroyItem(INVENTORY_SLOT_BAG_0, i, update);

						if (remcount >= count)
							return;
					}
				} else {
					ItemRemovedQuestCheck(pItem->GetEntry(), count - remcount);
					pItem->SetCount(pItem->GetCount() - count + remcount);
					if (IsInWorld() & update)
						pItem->SendUpdateToPlayer(this);
					pItem->SetState(ITEM_CHANGED, this);
					return;
				}
			}
		}
	}
}

void Player::DestroyZoneLimitedItem(bool update, uint32 new_zone) {
	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
			"STORAGE: DestroyZoneLimitedItem in map %u and area %u", GetMapId(),
			new_zone);

	// in inventory
	for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
		if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
				DestroyItem(INVENTORY_SLOT_BAG_0, i, update);

	for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
		if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
				DestroyItem(INVENTORY_SLOT_BAG_0, i, update);

	// in inventory bags
	for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
		if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			for (uint32 j = 0; j < pBag->GetBagSize(); j++)
				if (Item* pItem = pBag->GetItemByPos(j))
					if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(),
							new_zone))
						DestroyItem(i, j, update);

	// in equipment and bag list
	for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
		if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
				DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
}

void Player::DestroyConjuredItems(bool update) {
	// used when entering arena
	// destroys all conjured items
	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "STORAGE: DestroyConjuredItems");

	// in inventory
	for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
		if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem->IsConjuredConsumable())
				DestroyItem(INVENTORY_SLOT_BAG_0, i, update);

	// in inventory bags
	for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
		if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			for (uint32 j = 0; j < pBag->GetBagSize(); j++)
				if (Item* pItem = pBag->GetItemByPos(j))
					if (pItem->IsConjuredConsumable())
						DestroyItem(i, j, update);

	// in equipment and bag list
	for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
		if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem->IsConjuredConsumable())
				DestroyItem(INVENTORY_SLOT_BAG_0, i, update);
}

Item* Player::GetItemByEntry(uint32 entry) const {
	// in inventory
	for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) {
		if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem->GetEntry() == entry)
				return pItem;
	}

	for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) {
		if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i)) {
			for (uint32 j = 0; j < pBag->GetBagSize(); ++j) {
				if (Item* pItem = pBag->GetItemByPos(j))
					if (pItem->GetEntry() == entry)
						return pItem;
			}
		}
	}

	for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; ++i) {
		if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem->GetEntry() == entry)
				return pItem;
	}

	return NULL;
}

void Player::DestroyItemCount(Item* pItem, uint32 &count, bool update) {
	if (!pItem)
		return;

	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
			"STORAGE: DestroyItemCount item (GUID: %u, Entry: %u) count = %u",
			pItem->GetGUIDLow(), pItem->GetEntry(), count);

	if (pItem->GetCount() <= count) {
		count -= pItem->GetCount();

		DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), update);
	} else {
		ItemRemovedQuestCheck(pItem->GetEntry(), count);
		pItem->SetCount(pItem->GetCount() - count);
		count = 0;
		if (IsInWorld() & update)
			pItem->SendUpdateToPlayer(this);
		pItem->SetState(ITEM_CHANGED, this);
	}
}

void Player::SplitItem(uint16 src, uint16 dst, uint32 count) {
	uint8 srcbag = src >> 8;
	uint8 srcslot = src & 255;

	uint8 dstbag = dst >> 8;
	uint8 dstslot = dst & 255;

	Item *pSrcItem = GetItemByPos(srcbag, srcslot);
	if (!pSrcItem) {
		SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL);
		return;
	}

	if (pSrcItem->m_lootGenerated) // prevent split looting item (item
	{
		//best error message found for attempting to split while looting
		SendEquipError(EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL);
		return;
	}

	// not let split all items (can be only at cheating)
	if (pSrcItem->GetCount() == count) {
		SendEquipError(EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL);
		return;
	}

	// not let split more existed items (can be only at cheating)
	if (pSrcItem->GetCount() < count) {
		SendEquipError(EQUIP_ERR_TRIED_TO_SPLIT_MORE_THAN_COUNT, pSrcItem,
				NULL);
		return;
	}

	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
			"STORAGE: SplitItem bag = %u, slot = %u, item = %u, count = %u",
			dstbag, dstslot, pSrcItem->GetEntry(), count);
	Item *pNewItem = pSrcItem->CloneItem(count, this);
	if (!pNewItem) {
		SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL);
		return;
	}

	if (IsInventoryPos(dst)) {
		// change item amount before check (for unique max count check)
		pSrcItem->SetCount(pSrcItem->GetCount() - count);

		ItemPosCountVec dest;
		uint8 msg = CanStoreItem(dstbag, dstslot, dest, pNewItem, false);
		if (msg != EQUIP_ERR_OK) {
			delete pNewItem;
			pSrcItem->SetCount(pSrcItem->GetCount() + count);
			SendEquipError(msg, pSrcItem, NULL);
			return;
		}

		if (IsInWorld())
			pSrcItem->SendUpdateToPlayer(this);
		pSrcItem->SetState(ITEM_CHANGED, this);
		StoreItem(dest, pNewItem, true);
	} else if (IsBankPos(dst)) {
		// change item amount before check (for unique max count check)
		pSrcItem->SetCount(pSrcItem->GetCount() - count);

		ItemPosCountVec dest;
		uint8 msg = CanBankItem(dstbag, dstslot, dest, pNewItem, false);
		if (msg != EQUIP_ERR_OK) {
			delete pNewItem;
			pSrcItem->SetCount(pSrcItem->GetCount() + count);
			SendEquipError(msg, pSrcItem, NULL);
			return;
		}

		if (IsInWorld())
			pSrcItem->SendUpdateToPlayer(this);
		pSrcItem->SetState(ITEM_CHANGED, this);
		BankItem(dest, pNewItem, true);
	} else if (IsEquipmentPos(dst)) {
		// change item amount before check (for unique max count check), provide space for splitted items
		pSrcItem->SetCount(pSrcItem->GetCount() - count);

		uint16 dest;
		uint8 msg = CanEquipItem(dstslot, dest, pNewItem, false);
		if (msg != EQUIP_ERR_OK) {
			delete pNewItem;
			pSrcItem->SetCount(pSrcItem->GetCount() + count);
			SendEquipError(msg, pSrcItem, NULL);
			return;
		}

		if (IsInWorld())
			pSrcItem->SendUpdateToPlayer(this);
		pSrcItem->SetState(ITEM_CHANGED, this);
		EquipItem(dest, pNewItem, true);
		AutoUnequipOffhandIfNeed();
	}
}

void Player::SwapItem(uint16 src, uint16 dst) {
	uint8 srcbag = src >> 8;
	uint8 srcslot = src & 255;

	uint8 dstbag = dst >> 8;
	uint8 dstslot = dst & 255;

	Item *pSrcItem = GetItemByPos(srcbag, srcslot);
	Item *pDstItem = GetItemByPos(dstbag, dstslot);

	if (!pSrcItem)
		return;

	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
			"STORAGE: SwapItem bag = %u, slot = %u, item = %u", dstbag, dstslot,
			pSrcItem->GetEntry());

	if (!isAlive()) {
		SendEquipError(EQUIP_ERR_YOU_ARE_DEAD, pSrcItem, pDstItem);
		return;
	}

	// SRC checks

	if (pSrcItem->m_lootGenerated) // prevent swap looting item
	{
		//best error message found for attempting to swap while looting
		SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, pSrcItem, NULL);
		return;
	}

	// check unequip potability for equipped items and bank bags
	if (IsEquipmentPos(src) || IsBagPos(src)) {
		// bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later)
		uint8 msg = CanUnequipItem(
				src,
				!IsBagPos(src) || IsBagPos(dst)
						|| (pDstItem && pDstItem->IsBag()
								&& ((Bag*) pDstItem)->IsEmpty()));
		if (msg != EQUIP_ERR_OK) {
			SendEquipError(msg, pSrcItem, pDstItem);
			return;
		}
	}

	// prevent put equipped/bank bag in self
	if (IsBagPos(src) && srcslot == dstbag) {
		SendEquipError(EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG, pSrcItem,
				pDstItem);
		return;
	}

	// prevent equipping bag in the same slot from its inside
	if (IsBagPos(dst) && srcbag == dstslot) {
		SendEquipError(EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pSrcItem, pDstItem);
		return;
	}

	// DST checks

	if (pDstItem) {
		if (pDstItem->m_lootGenerated) // prevent swap looting item
		{
			//best error message found for attempting to swap while looting
			SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, pDstItem, NULL);
			return;
		}

		// check unequip potability for equipped items and bank bags
		if (IsEquipmentPos(dst) || IsBagPos(dst)) {
			// bags can be swapped with empty bag slots, or with empty bag (items move possibility checked later)
			uint8 msg = CanUnequipItem(
					dst,
					!IsBagPos(dst) || IsBagPos(src)
							|| (pSrcItem->IsBag()
									&& ((Bag*) pSrcItem)->IsEmpty()));
			if (msg != EQUIP_ERR_OK) {
				SendEquipError(msg, pSrcItem, pDstItem);
				return;
			}
		}
	}

	// NOW this is or item move (swap with empty), or swap with another item (including bags in bag possitions)
	// or swap empty bag with another empty or not empty bag (with items exchange)

	// Move case
	if (!pDstItem) {
		if (IsInventoryPos(dst)) {
			ItemPosCountVec dest;
			uint8 msg = CanStoreItem(dstbag, dstslot, dest, pSrcItem, false);
			if (msg != EQUIP_ERR_OK) {
				SendEquipError(msg, pSrcItem, NULL);
				return;
			}

			RemoveItem(srcbag, srcslot, true);
			StoreItem(dest, pSrcItem, true);
		} else if (IsBankPos(dst)) {
			ItemPosCountVec dest;
			uint8 msg = CanBankItem(dstbag, dstslot, dest, pSrcItem, false);
			if (msg != EQUIP_ERR_OK) {
				SendEquipError(msg, pSrcItem, NULL);
				return;
			}

			RemoveItem(srcbag, srcslot, true);
			BankItem(dest, pSrcItem, true);
		} else if (IsEquipmentPos(dst)) {
			uint16 dest;
			uint8 msg = CanEquipItem(dstslot, dest, pSrcItem, false);
			if (msg != EQUIP_ERR_OK) {
				SendEquipError(msg, pSrcItem, NULL);
				return;
			}

			RemoveItem(srcbag, srcslot, true);
			EquipItem(dest, pSrcItem, true);
			AutoUnequipOffhandIfNeed();
		}

		return;
	}

	// attempt merge to / fill target item
	if (!pSrcItem->IsBag() && !pDstItem->IsBag()) {
		uint8 msg;
		ItemPosCountVec sDest;
		uint16 eDest = 0;
		if (IsInventoryPos(dst))
			msg = CanStoreItem(dstbag, dstslot, sDest, pSrcItem, false);
		else if (IsBankPos(dst))
			msg = CanBankItem(dstbag, dstslot, sDest, pSrcItem, false);
		else if (IsEquipmentPos(dst))
			msg = CanEquipItem(dstslot, eDest, pSrcItem, false);
		else
			return;

		// can be merge/fill
		if (msg == EQUIP_ERR_OK) {
			if (pSrcItem->GetCount() + pDstItem->GetCount()
					<= pSrcItem->GetProto()->GetMaxStackSize()) {
				RemoveItem(srcbag, srcslot, true);

				if (IsInventoryPos(dst))
					StoreItem(sDest, pSrcItem, true);
				else if (IsBankPos(dst))
					BankItem(sDest, pSrcItem, true);
				else if (IsEquipmentPos(dst)) {
					EquipItem(eDest, pSrcItem, true);
					AutoUnequipOffhandIfNeed();
				}
			} else {
				pSrcItem->SetCount(
						pSrcItem->GetCount() + pDstItem->GetCount()
								- pSrcItem->GetProto()->GetMaxStackSize());
				pDstItem->SetCount(pSrcItem->GetProto()->GetMaxStackSize());
				pSrcItem->SetState(ITEM_CHANGED, this);
				pDstItem->SetState(ITEM_CHANGED, this);
				if (IsInWorld()) {
					pSrcItem->SendUpdateToPlayer(this);
					pDstItem->SendUpdateToPlayer(this);
				}
			}
			SendRefundInfo(pDstItem);
			return;
		}
	}

	// impossible merge/fill, do real swap
	uint8 msg = EQUIP_ERR_OK;

	// check src->dest move possibility
	ItemPosCountVec sDest;
	uint16 eDest = 0;
	if (IsInventoryPos(dst))
		msg = CanStoreItem(dstbag, dstslot, sDest, pSrcItem, true);
	else if (IsBankPos(dst))
		msg = CanBankItem(dstbag, dstslot, sDest, pSrcItem, true);
	else if (IsEquipmentPos(dst)) {
		msg = CanEquipItem(dstslot, eDest, pSrcItem, true);
		if (msg == EQUIP_ERR_OK)
			msg = CanUnequipItem(eDest, true);
	}

	if (msg != EQUIP_ERR_OK) {
		SendEquipError(msg, pSrcItem, pDstItem);
		return;
	}

	// check dest->src move possibility
	ItemPosCountVec sDest2;
	uint16 eDest2 = 0;
	if (IsInventoryPos(src))
		msg = CanStoreItem(srcbag, srcslot, sDest2, pDstItem, true);
	else if (IsBankPos(src))
		msg = CanBankItem(srcbag, srcslot, sDest2, pDstItem, true);
	else if (IsEquipmentPos(src)) {
		msg = CanEquipItem(srcslot, eDest2, pDstItem, true);
		if (msg == EQUIP_ERR_OK)
			msg = CanUnequipItem(eDest2, true);
	}

	if (msg != EQUIP_ERR_OK) {
		SendEquipError(msg, pDstItem, pSrcItem);
		return;
	}

	// Check bag swap with item exchange (one from empty in not bag possition (equipped (not possible in fact) or store)
	if (pSrcItem->IsBag() && pDstItem->IsBag()) {
		Bag* emptyBag = NULL;
		Bag* fullBag = NULL;
		if (((Bag*) pSrcItem)->IsEmpty() && !IsBagPos(src)) {
			emptyBag = (Bag*) pSrcItem;
			fullBag = (Bag*) pDstItem;
		} else if (((Bag*) pDstItem)->IsEmpty() && !IsBagPos(dst)) {
			emptyBag = (Bag*) pDstItem;
			fullBag = (Bag*) pSrcItem;
		}

		// bag swap (with items exchange) case
		if (emptyBag && fullBag) {
			ItemPrototype const* emptyProto = emptyBag->GetProto();

			uint32 count = 0;

			for (uint32 i = 0; i < fullBag->GetBagSize(); ++i) {
				Item *bagItem = fullBag->GetItemByPos(i);
				if (!bagItem)
					continue;

				ItemPrototype const* bagItemProto = bagItem->GetProto();
				if (!bagItemProto
						|| !ItemCanGoIntoBag(bagItemProto, emptyProto)) {
					// one from items not go to empty target bag
					SendEquipError(EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG,
							pSrcItem, pDstItem);
					return;
				}

				++count;
			}

			if (count > emptyBag->GetBagSize()) {
				// too small targeted bag
				SendEquipError(EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pSrcItem,
						pDstItem);
				return;
			}

			// Items swap
			count = 0; // will pos in new bag
			for (uint32 i = 0; i < fullBag->GetBagSize(); ++i) {
				Item *bagItem = fullBag->GetItemByPos(i);
				if (!bagItem)
					continue;

				fullBag->RemoveItem(i, true);
				emptyBag->StoreItem(count, bagItem, true);
				bagItem->SetState(ITEM_CHANGED, this);

				++count;
			}
		}
	}

	// now do moves, remove...
	RemoveItem(dstbag, dstslot, false);
	RemoveItem(srcbag, srcslot, false);

	// add to dest
	if (IsInventoryPos(dst))
		StoreItem(sDest, pSrcItem, true);
	else if (IsBankPos(dst))
		BankItem(sDest, pSrcItem, true);
	else if (IsEquipmentPos(dst))
		EquipItem(eDest, pSrcItem, true);

	// add to src
	if (IsInventoryPos(src))
		StoreItem(sDest2, pDstItem, true);
	else if (IsBankPos(src))
		BankItem(sDest2, pDstItem, true);
	else if (IsEquipmentPos(src))
		EquipItem(eDest2, pDstItem, true);

	// if player is moving bags and is looting an item inside this bag
	// release the loot
	if (GetLootGUID()) {
		bool released = false;
		if (IsBagPos(src)) {
			Bag* bag = (Bag*) pSrcItem;
			for (uint32 i = 0; i < bag->GetBagSize(); ++i) {
				if (Item *bagItem = bag->GetItemByPos(i)) {
					if (bagItem->m_lootGenerated) {
						m_session->DoLootRelease(GetLootGUID());
						released = true; // so we don't need to look at dstBag
						break;
					}
				}
			}
		}

		if (!released && IsBagPos(dst) && pDstItem) {
			Bag* bag = (Bag*) pDstItem;
			for (uint32 i = 0; i < bag->GetBagSize(); ++i) {
				if (Item *bagItem = bag->GetItemByPos(i)) {
					if (bagItem->m_lootGenerated) {
						m_session->DoLootRelease(GetLootGUID());
						released = true; // not realy needed here
						break;
					}
				}
			}
		}
	}

	AutoUnequipOffhandIfNeed();
}

void Player::AddItemToBuyBackSlot(Item *pItem) {
	if (pItem) {
		uint32 slot = m_currentBuybackSlot;
		// if current back slot non-empty search oldest or free
		if (m_items[slot]) {
			uint32 oldest_time = GetUInt32Value(
					PLAYER_FIELD_BUYBACK_TIMESTAMP_1);
			uint32 oldest_slot = BUYBACK_SLOT_START;

			for (uint32 i = BUYBACK_SLOT_START + 1; i < BUYBACK_SLOT_END; ++i) {
				// found empty
				if (!m_items[i]) {
					slot = i;
					break;
				}

				uint32 i_time = GetUInt32Value(
						PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + i
								- BUYBACK_SLOT_START);

				if (oldest_time > i_time) {
					oldest_time = i_time;
					oldest_slot = i;
				}
			}

			// find oldest
			slot = oldest_slot;
		}

		RemoveItemFromBuyBackSlot(slot, true);
		sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
				"STORAGE: AddItemToBuyBackSlot item = %u, slot = %u",
				pItem->GetEntry(), slot);

		m_items[slot] = pItem;
		time_t base = time(NULL);
		uint32 etime = uint32(base - m_logintime + (30 * 3600));
		uint32 eslot = slot - BUYBACK_SLOT_START;

		SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2),
				pItem->GetGUID());
		if (ItemPrototype const *pProto = pItem->GetProto())
			SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot,
					pProto->SellPrice * pItem->GetCount());
		else
			SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0);
		SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot,
				(uint32) etime);

		// move to next (for non filled list is move most optimized choice)
		if (m_currentBuybackSlot < BUYBACK_SLOT_END - 1)
			++m_currentBuybackSlot;
	}
}

Item* Player::GetItemFromBuyBackSlot(uint32 slot) {
	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
			"STORAGE: GetItemFromBuyBackSlot slot = %u", slot);
	if (slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END)
		return m_items[slot];
	return NULL;
}

void Player::RemoveItemFromBuyBackSlot(uint32 slot, bool del) {
	sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
			"STORAGE: RemoveItemFromBuyBackSlot slot = %u", slot);
	if (slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END) {
		Item *pItem = m_items[slot];
		if (pItem) {
			pItem->RemoveFromWorld();
			if (del)
				pItem->SetState(ITEM_REMOVED, this);
		}

		m_items[slot] = NULL;

		uint32 eslot = slot - BUYBACK_SLOT_START;
		SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + (eslot * 2), 0);
		SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0);
		SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, 0);

		// if current backslot is filled set to now free slot
		if (m_items[m_currentBuybackSlot])
			m_currentBuybackSlot = slot;
	}
}

void Player::SendEquipError(uint8 msg, Item* pItem, Item *pItem2,
		uint32 itemid) {
	sLog->outDebug(LOG_FILTER_NETWORKIO,
			"World: Sent SMSG_INVENTORY_CHANGE_FAILURE (%u)", msg);
	WorldPacket data(SMSG_INVENTORY_CHANGE_FAILURE,
			(msg == EQUIP_ERR_CANT_EQUIP_LEVEL_I ? 22 : 18));
	data << uint8(msg);

	if (msg != EQUIP_ERR_OK) {
		data << uint64(pItem ? pItem->GetGUID() : 0);
		data << uint64(pItem2 ? pItem2->GetGUID() : 0);
		data << uint8(0); // bag type subclass, used with EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM and EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG2

		switch (msg) {
		case EQUIP_ERR_CANT_EQUIP_LEVEL_I:
		case EQUIP_ERR_PURCHASE_LEVEL_TOO_LOW: {
			ItemPrototype const* proto =
					pItem ? pItem->GetProto() : ObjectMgr::GetItemPrototype(
									itemid);
			data << uint32(proto ? proto->RequiredLevel : 0);
			break;
		}
		case EQUIP_ERR_EVENT_AUTOEQUIP_BIND_CONFIRM: // no idea about this one...
		{
			data << uint64(0);
			data << uint32(0);
			data << uint64(0);
			break;
		}
		case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED:
		case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_SOCKETED_EXCEEDED:
		case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED: {
			ItemPrototype const* proto =
					pItem ? pItem->GetProto() : ObjectMgr::GetItemPrototype(
									itemid);
			data << uint32(proto ? proto->ItemLimitCategory : 0);
			break;
		}
		default:
			break;
		}
	}
	GetSession()->SendPacket(&data);
}

void Player::SendBuyError(uint8 msg, Creature* pCreature, uint32 item,
		uint32 param) {
	sLog->outDebug(LOG_FILTER_NETWORKIO, "World: Sent SMSG_BUY_FAILED");
	WorldPacket data(SMSG_BUY_FAILED, (8 + 4 + 4 + 1 + 2), true);
	data << uint64(pCreature ? pCreature->GetGUID() : 0);
	data << uint32(item);
	if (param > 0)
		data << uint32(param);
	data << uint8(msg);
	GetSession()->SendPacket(&data);
}

void Player::SendSellError(uint8 msg, Creature* pCreature, uint64 guid,
		uint32 param) {
	sLog->outDebug(LOG_FILTER_NETWORKIO, "World: Sent SMSG_SELL_ITEM");
	WorldPacket data(SMSG_SELL_ITEM, (8 + 8 + (param ? 4 : 0) + 1)); // last check 2.0.10
	data << uint64(pCreature ? pCreature->GetGUID() : 0);
	data << uint64(guid);
	if (param > 0)
		data << uint32(param);
	data << uint8(msg);
	GetSession()->SendPacket(&data);
}

void Player::TradeCancel(bool sendback) {
	if (m_trade) {
		Player* trader = m_trade->GetTrader();

		// send yellow "Trade canceled" message to both traders
		if (sendback)
			GetSession()->SendCancelTrade();

		trader->GetSession()->SendCancelTrade();

		// cleanup
		delete m_trade;
		m_trade = NULL;
		delete trader->m_trade;
		trader->m_trade = NULL;
	}
}

void Player::UpdateSoulboundTradeItems() {
	if (m_itemSoulboundTradeable.empty())
		return;

	// also checks for garbage data
	for (ItemDurationList::iterator itr = m_itemSoulboundTradeable.begin();
			itr != m_itemSoulboundTradeable.end();) {
		if (!*itr) {
			itr = m_itemSoulboundTradeable.erase(itr++);
			continue;
		}
		if ((*itr)->GetOwnerGUID() != GetGUID()) {
			itr = m_itemSoulboundTradeable.erase(itr++);
			continue;
		}
		if ((*itr)->CheckSoulboundTradeExpire()) {
			itr = m_itemSoulboundTradeable.erase(itr++);
			continue;
		}
		++itr;
	}
}

void Player::RemoveTradeableItem(Item* item) {
	for (ItemDurationList::iterator itr = m_itemSoulboundTradeable.begin();
			itr != m_itemSoulboundTradeable.end(); ++itr) {
		if ((*itr) == item) {
			m_itemSoulboundTradeable.erase(itr);
			break;
		}
	}
}

void Player::UpdateItemDuration(uint32 time, bool realtimeonly) {
	if (m_itemDuration.empty())
		return;

	sLog->outDebug(LOG_FILTER_PLAYER_SKILLS,
			"Player::UpdateItemDuration(%u, %u)", time, realtimeonly);

	for (ItemDurationList::const_iterator itr = m_itemDuration.begin();
			itr != m_itemDuration.end();) {
		Item* item = *itr;
		++itr; // current element can be erased in UpdateDuration

		if ((realtimeonly && item->GetProto()->Duration < 0) || !realtimeonly)
			item->UpdateDuration(this, time);
	}
}

void Player::UpdateEnchantTime(uint32 time) {
	for (EnchantDurationList::iterator itr = m_enchantDuration.begin(), next;
			itr != m_enchantDuration.end(); itr = next) {
		ASSERT(itr->item);
		next = itr;
		if (!itr->item->GetEnchantmentId(itr->slot)) {
			next = m_enchantDuration.erase(itr);
		} else if (itr->leftduration <= time) {
			ApplyEnchantment(itr->item, itr->slot, false, false);
			itr->item->ClearEnchantment(itr->slot);
			next = m_enchantDuration.erase(itr);
		} else if (itr->leftduration > time) {
			itr->leftduration -= time;
			++next;
		}
	}
}

void Player::AddEnchantmentDurations(Item *item) {
	for (int x = 0; x < MAX_ENCHANTMENT_SLOT; ++x) {
		if (!item->GetEnchantmentId(EnchantmentSlot(x)))
			continue;

		uint32 duration = item->GetEnchantmentDuration(EnchantmentSlot(x));
		if (duration > 0)
			AddEnchantmentDuration(item, EnchantmentSlot(x), duration);
	}
}

void Player::RemoveEnchantmentDurations(Item *item) {
	for (EnchantDurationList::iterator itr = m_enchantDuration.begin();
			itr != m_enchantDuration.end();) {
		if (itr->item == item) {
			// save duration in item
			item->SetEnchantmentDuration(EnchantmentSlot(itr->slot),
					itr->leftduration, this);
			itr = m_enchantDuration.erase(itr);
		} else
			++itr;
	}
}

void Player::RemoveArenaEnchantments(EnchantmentSlot slot) {
	// remove enchantments from equipped items first to clean up the m_enchantDuration list
	for (EnchantDurationList::iterator itr = m_enchantDuration.begin(), next;
			itr != m_enchantDuration.end(); itr = next) {
		next = itr;
		if (itr->slot == slot) {
			if (itr->item && itr->item->GetEnchantmentId(slot)) {
				// Poisons and DK runes are enchants which are allowed on arenas
				if (sSpellMgr->IsArenaAllowedEnchancment(
						itr->item->GetEnchantmentId(slot))) {
					++next;
					continue;
				}
				// remove from stats
				ApplyEnchantment(itr->item, slot, false, false);
				// remove visual
				itr->item->ClearEnchantment(slot);
			}
			// remove from update list
			next = m_enchantDuration.erase(itr);
		} else
			++next;
	}

	// remove enchants from inventory items
	// NOTE: no need to remove these from stats, since these aren't equipped
	// in inventory
	for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i)
		if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			if (pItem->GetEnchantmentId(slot))
				pItem->ClearEnchantment(slot);

	// in inventory bags
	for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
		if (Bag* pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0, i))
			for (uint32 j = 0; j < pBag->GetBagSize(); j++)
				if (Item* pItem = pBag->GetItemByPos(j))
					if (pItem->GetEnchantmentId(slot))
						pItem->ClearEnchantment(slot);
}

// duration == 0 will remove item enchant
void Player::AddEnchantmentDuration(Item *item, EnchantmentSlot slot,
		uint32 duration) {
	if (!item)
		return;

	if (slot >= MAX_ENCHANTMENT_SLOT)
		return;

	for (EnchantDurationList::iterator itr = m_enchantDuration.begin();
			itr != m_enchantDuration.end(); ++itr) {
		if (itr->item == item && itr->slot == slot) {
			itr->item->SetEnchantmentDuration(itr->slot, itr->leftduration,
					this);
			m_enchantDuration.erase(itr);
			break;
		}
	}
	if (item && duration > 0) {
		GetSession()->SendItemEnchantTimeUpdate(GetGUID(), item->GetGUID(),
				slot, uint32(duration / 1000));
		m_enchantDuration.push_back(EnchantDuration(item, slot, duration));
	}
}

void Player::ApplyEnchantment(Item *item, bool apply) {
	for (uint32 slot = 0; slot < MAX_ENCHANTMENT_SLOT; ++slot)
		ApplyEnchantment(item, EnchantmentSlot(slot), apply);
}

void Player::ApplyEnchantment(Item *item, EnchantmentSlot slot, bool apply,
		bool apply_dur, bool ignore_condition) {
	if (!item || !item->IsEquipped())
		return;

	if (slot >= MAX_ENCHANTMENT_SLOT)
		return;

	if (slot == REFORGE_ENCHANTMENT_SLOT) {
		ApplyItemReforge(item, apply);
		return;
	}

	uint32 enchant_id = item->GetEnchantmentId(slot);
	if (!enchant_id)
		return;

	SpellItemEnchantmentEntry const *pEnchant =
			sSpellItemEnchantmentStore.LookupEntry(enchant_id);
	if (!pEnchant)
		return;

	if (!ignore_condition && pEnchant->EnchantmentCondition
			&& !EnchantmentFitsRequirements(pEnchant->EnchantmentCondition, -1))
		return;

	if (pEnchant->requiredLevel > getLevel())
		return;

	if (pEnchant->requiredSkill > 0
			&& pEnchant->requiredSkillValue
					> GetSkillValue(pEnchant->requiredSkill))
		return;

	// If we're dealing with a gem inside a prismatic socket we need to check the prismatic socket requirements
	// rather than the gem requirements itself. If the socket has no color it is a prismatic socket.
	if ((slot == SOCK_ENCHANTMENT_SLOT || slot == SOCK_ENCHANTMENT_SLOT_2
			|| slot == SOCK_ENCHANTMENT_SLOT_3)
			&& !item->GetProto()->Socket[slot - SOCK_ENCHANTMENT_SLOT].Color) {
		// Check if the requirements for the prismatic socket are met before applying the gem stats
		SpellItemEnchantmentEntry const *pPrismaticEnchant =
				sSpellItemEnchantmentStore.LookupEntry(
						item->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT));
		if (!pPrismaticEnchant
				|| (pPrismaticEnchant->requiredSkill > 0
						&& pPrismaticEnchant->requiredSkillValue
								> GetSkillValue(
										pPrismaticEnchant->requiredSkill)))
			return;
	}

	if (!item->IsBroken()) {
		for (int s = 0; s < MAX_ITEM_ENCHANTMENT_EFFECTS; ++s) {
			uint32 enchant_display_type = pEnchant->type[s];
			uint32 enchant_amount = pEnchant->amount[s];
			uint32 enchant_spell_id = pEnchant->spellid[s];

			switch (enchant_display_type) {
			case ITEM_ENCHANTMENT_TYPE_NONE:
				break;
			case ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL:
				// processed in Player::CastItemCombatSpell
				break;
			case ITEM_ENCHANTMENT_TYPE_DAMAGE:
				if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND)
					HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE,
							float(enchant_amount), apply);
				else if (item->GetSlot() == EQUIPMENT_SLOT_OFFHAND)
					HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE,
							float(enchant_amount), apply);
				else if (item->GetSlot() == EQUIPMENT_SLOT_RANGED)
					HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE,
							float(enchant_amount), apply);
				break;
			case ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL:
				if (enchant_spell_id) {
					if (apply) {
						int32 basepoints = 0;
						// Random Property Exist - try found basepoints for spell (basepoints depends from item suffix factor)
						if (item->GetItemRandomPropertyId()) {
							ItemRandomSuffixEntry const *item_rand =
									sItemRandomSuffixStore.LookupEntry(
											abs(
													item->GetItemRandomPropertyId()));
							if (item_rand) {
								// Search enchant_amount
								for (int k = 0;
										k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k) {
									if (item_rand->enchant_id[k]
											== enchant_id) {
										basepoints =
												int32(
														(item_rand->prefix[k]
																* item->GetItemSuffixFactor())
																/ 10000);
										break;
									}
								}
							}
						}
						// Cast custom spell vs all equal basepoints getted from enchant_amount
						if (basepoints)
							CastCustomSpell(this, enchant_spell_id, &basepoints,
									&basepoints, &basepoints, true, item);
						else
							CastSpell(this, enchant_spell_id, true, item);
					} else
						RemoveAurasDueToItemSpell(item, enchant_spell_id);
				}
				break;
			case ITEM_ENCHANTMENT_TYPE_RESISTANCE:
				if (!enchant_amount) {
					ItemRandomSuffixEntry const *item_rand =
							sItemRandomSuffixStore.LookupEntry(
									abs(item->GetItemRandomPropertyId()));
					if (item_rand) {
						for (int k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k) {
							if (item_rand->enchant_id[k] == enchant_id) {
								enchant_amount = uint32(
										(item_rand->prefix[k]
												* item->GetItemSuffixFactor())
												/ 10000);
								break;
							}
						}
					}
				}

				HandleStatModifier(
						UnitMods(UNIT_MOD_RESISTANCE_START + enchant_spell_id),
						TOTAL_VALUE, float(enchant_amount), apply);
				break;
			case ITEM_ENCHANTMENT_TYPE_STAT: {
				if (!enchant_amount) {
					ItemRandomSuffixEntry const *item_rand_suffix =
							sItemRandomSuffixStore.LookupEntry(
									abs(item->GetItemRandomPropertyId()));
					if (item_rand_suffix) {
						for (int k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k) {
							if (item_rand_suffix->enchant_id[k] == enchant_id) {
								enchant_amount = uint32(
										(item_rand_suffix->prefix[k]
												* item->GetItemSuffixFactor())
												/ 10000);
								break;
							}
						}
					}
				}

				sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
						"Adding %u to stat nb %u", enchant_amount,
						enchant_spell_id);
				switch (enchant_spell_id) {
				case ITEM_MOD_MANA:
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u MANA",
							enchant_amount);
					HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE,
							float(enchant_amount), apply);
					break;
				case ITEM_MOD_HEALTH:
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u HEALTH",
							enchant_amount);
					HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE,
							float(enchant_amount), apply);
					break;
				case ITEM_MOD_AGILITY:
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u AGILITY",
							enchant_amount);
					HandleStatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE,
							float(enchant_amount), apply);
					ApplyStatBuffMod(STAT_AGILITY, (float) enchant_amount,
							apply);
					break;
				case ITEM_MOD_STRENGTH:
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u STRENGTH",
							enchant_amount);
					HandleStatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE,
							float(enchant_amount), apply);
					ApplyStatBuffMod(STAT_STRENGTH, (float) enchant_amount,
							apply);
					break;
				case ITEM_MOD_INTELLECT:
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u INTELLECT",
							enchant_amount);
					HandleStatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE,
							float(enchant_amount), apply);
					ApplyStatBuffMod(STAT_INTELLECT, (float) enchant_amount,
							apply);
					break;
				case ITEM_MOD_SPIRIT:
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u SPIRIT",
							enchant_amount);
					HandleStatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE,
							float(enchant_amount), apply);
					ApplyStatBuffMod(STAT_SPIRIT, (float) enchant_amount,
							apply);
					break;
				case ITEM_MOD_STAMINA:
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u STAMINA",
							enchant_amount);
					HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE,
							float(enchant_amount), apply);
					ApplyStatBuffMod(STAT_STAMINA, (float) enchant_amount,
							apply);
					break;
				case ITEM_MOD_DEFENSE_SKILL_RATING:
					ApplyRatingMod(CR_DEFENSE_SKILL, enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u DEFENCE",
							enchant_amount);
					break;
				case ITEM_MOD_DODGE_RATING:
					ApplyRatingMod(CR_DODGE, enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u DODGE",
							enchant_amount);
					break;
				case ITEM_MOD_PARRY_RATING:
					ApplyRatingMod(CR_PARRY, enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u PARRY",
							enchant_amount);
					break;
				case ITEM_MOD_BLOCK_RATING:
					ApplyRatingMod(CR_BLOCK, enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u SHIELD_BLOCK",
							enchant_amount);
					break;
				case ITEM_MOD_HIT_MELEE_RATING:
					ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u MELEE_HIT",
							enchant_amount);
					break;
				case ITEM_MOD_HIT_RANGED_RATING:
					ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u RANGED_HIT",
							enchant_amount);
					break;
				case ITEM_MOD_HIT_SPELL_RATING:
					ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u SPELL_HIT",
							enchant_amount);
					break;
				case ITEM_MOD_CRIT_MELEE_RATING:
					ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u MELEE_CRIT",
							enchant_amount);
					break;
				case ITEM_MOD_CRIT_RANGED_RATING:
					ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u RANGED_CRIT",
							enchant_amount);
					break;
				case ITEM_MOD_CRIT_SPELL_RATING:
					ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u SPELL_CRIT",
							enchant_amount);
					break;
//                        Values from ITEM_STAT_MELEE_HA_RATING to ITEM_MOD_HASTE_RANGED_RATING are never used
//                        in Enchantments
//                        case ITEM_MOD_HIT_TAKEN_MELEE_RATING:
//                            ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply);
//                            break;
//                        case ITEM_MOD_HIT_TAKEN_RANGED_RATING:
//                            ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply);
//                            break;
//                        case ITEM_MOD_HIT_TAKEN_SPELL_RATING:
//                            ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply);
//                            break;
//                        case ITEM_MOD_CRIT_TAKEN_MELEE_RATING:
//                            ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
//                            break;
//                        case ITEM_MOD_CRIT_TAKEN_RANGED_RATING:
//                            ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
//                            break;
//                        case ITEM_MOD_CRIT_TAKEN_SPELL_RATING:
//                            ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
//                            break;
//                        case ITEM_MOD_HASTE_MELEE_RATING:
//                            ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply);
//                            break;
//                        case ITEM_MOD_HASTE_RANGED_RATING:
//                            ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply);
//                            break;
				case ITEM_MOD_HASTE_SPELL_RATING:
					ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply);
					break;
				case ITEM_MOD_HIT_RATING:
					ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply);
					ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply);
					ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u HIT",
							enchant_amount);
					break;
				case ITEM_MOD_CRIT_RATING:
					ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply);
					ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply);
					ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u CRITICAL",
							enchant_amount);
					break;
//                        Values ITEM_MOD_HIT_TAKEN_RATING and ITEM_MOD_CRIT_TAKEN_RATING are never used in Enchantment
//                        case ITEM_MOD_HIT_TAKEN_RATING:
//                            ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply);
//                            ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply);
//                            ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply);
//                            break;
//                        case ITEM_MOD_CRIT_TAKEN_RATING:
//                            ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
//                            ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
//                            ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
//                            break;
				case ITEM_MOD_RESILIENCE_RATING:
					ApplyRatingMod(CR_RESILIENCE_PLAYER_DAMAGE_TAKEN,
							enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u RESILIENCE",
							enchant_amount);
					break;
				case ITEM_MOD_HASTE_RATING:
					ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply);
					ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply);
					ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u HASTE",
							enchant_amount);
					break;
				case ITEM_MOD_EXPERTISE_RATING:
					ApplyRatingMod(CR_EXPERTISE, enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u EXPERTISE",
							enchant_amount);
					break;
				case ITEM_MOD_ATTACK_POWER:
					if (float(enchant_amount) > 0.f) {
						HandleStatModifier(UNIT_MOD_ATTACK_POWER_POS,
								TOTAL_VALUE, float(enchant_amount), apply);
						HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED_POS,
								TOTAL_VALUE, float(enchant_amount), apply);
					} else {
						HandleStatModifier(UNIT_MOD_ATTACK_POWER_NEG,
								TOTAL_VALUE, -float(enchant_amount), apply);
						HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED_NEG,
								TOTAL_VALUE, -float(enchant_amount), apply);
					}
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u ATTACK_POWER",
							enchant_amount);
					break;
				case ITEM_MOD_RANGED_ATTACK_POWER:
					if (float(enchant_amount) > 0.f)
						HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED_POS,
								TOTAL_VALUE, float(enchant_amount), apply);
					else
						HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED_NEG,
								TOTAL_VALUE, -float(enchant_amount), apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
							"+ %u RANGED_ATTACK_POWER", enchant_amount);
					break;
//                        case ITEM_MOD_FERAL_ATTACK_POWER:
//                            ApplyFeralAPBonus(enchant_amount, apply);
//                            sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u FERAL_ATTACK_POWER", enchant_amount);
//                            break;
				case ITEM_MOD_MANA_REGENERATION:
					ApplyManaRegenBonus(enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
							"+ %u MANA_REGENERATION", enchant_amount);
					break;
				case ITEM_MOD_ARMOR_PENETRATION_RATING:
					ApplyRatingMod(CR_ARMOR_PENETRATION, enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
							"+ %u ARMOR PENETRATION", enchant_amount);
					break;
				case ITEM_MOD_SPELL_POWER:
					ApplySpellPowerBonus(enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u SPELL_POWER",
							enchant_amount);
					break;
				case ITEM_MOD_HEALTH_REGEN:
					ApplyHealthRegenBonus(enchant_amount, apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
							"+ %u HEALTH_REGENERATION", enchant_amount);
					break;
				case ITEM_MOD_SPELL_PENETRATION:
					ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE,
							enchant_amount, apply);
					m_spellPenetrationItemMod +=
							apply ? int32(enchant_amount) : -int32(
											enchant_amount);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
							"+ %u SPELL_PENETRATION", enchant_amount);
					break;
				case ITEM_MOD_BLOCK_VALUE:
					HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD,
							float(enchant_amount), apply);
					sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u BLOCK_VALUE",
							enchant_amount);
					break;
				case ITEM_MOD_MASTERY_RATING:
					ApplyRatingMod(CR_MASTERY, int32(enchant_amount), apply);
					break;
				case ITEM_MOD_SPELL_HEALING_DONE: // deprecated
				case ITEM_MOD_SPELL_DAMAGE_DONE: // deprecated
				default:
					break;
				}
				break;
			}
			case ITEM_ENCHANTMENT_TYPE_TOTEM: // Shaman Rockbiter Weapon
			{
				if (getClass() == CLASS_SHAMAN) {
					float addValue = 0.0f;
					if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND) {
						addValue = float(
								enchant_amount * item->GetProto()->Delay
										/ 1000.0f);
						HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND,
								TOTAL_VALUE, addValue, apply);
					} else if (item->GetSlot() == EQUIPMENT_SLOT_OFFHAND) {
						addValue = float(
								enchant_amount * item->GetProto()->Delay
										/ 1000.0f);
						HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE,
								addValue, apply);
					}
				}
				break;
			}
			case ITEM_ENCHANTMENT_TYPE_USE_SPELL:
				// processed in Player::CastItemUseSpell
				break;
			case ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET:
				// nothing do..
				break;
			default:
				sLog->outError(
						"Unknown item enchantment (id = %d) display type: %d",
						enchant_id, enchant_display_type);
				break;
			} /*switch(enchant_display_type)*/
		} /*for*/
	}

	// visualize enchantment at player and equipped items
	if (slot == PERM_ENCHANTMENT_SLOT)
		SetUInt16Value(
				PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (item->GetSlot() * 2), 0,
				apply ? item->GetEnchantmentId(slot) : 0);

	if (slot == TEMP_ENCHANTMENT_SLOT)
		SetUInt16Value(
				PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (item->GetSlot() * 2), 1,
				apply ? item->GetEnchantmentId(slot) : 0);

	if (apply_dur) {
		if (apply) {
			// set duration
			uint32 duration = item->GetEnchantmentDuration(slot);
			if (duration > 0)
				AddEnchantmentDuration(item, slot, duration);
		} else {
			// duration == 0 will remove EnchantDuration
			AddEnchantmentDuration(item, slot, 0);
		}
	}
}

void Player::ApplyItemReforge(Item *item, bool apply) {
	if (!item || !item->IsEquipped() || item->IsBroken())
		return;

	uint32 reforge_id = item->GetEnchantmentId(REFORGE_ENCHANTMENT_SLOT);
	if (!reforge_id)
		return;

	ItemReforgeEntry const *reforge = sItemReforgeStore.LookupEntry(reforge_id);
	if (!reforge)
		return;

	int32 statBaseValue = 0;

	for (int32 i = 0; i < MAX_ITEM_PROTO_STATS; i++) {
		if (item->GetProto()->ItemStat[i].ItemStatType == reforge->newstat) {
			sLog->outError(
					"ApplyItemReforge : new stat %u already exists on item %u",
					reforge->newstat, item->GetEntry());
			return;
		}

		if (item->GetProto()->ItemStat[i].ItemStatType == reforge->oldstat)
			statBaseValue = item->GetProto()->ItemStat[i].ItemStatValue;
	}

	if (!statBaseValue) {
		sLog->outError("ApplyItemReforge : old stat %u not found on item %u",
				reforge->oldstat, item->GetEntry());
		return;
	}

	int32 statValue[2];
	int32 statType[2];

	statValue[0] = int32(statBaseValue * reforge->oldstat_coef); // old stat value: apply:minus unapply:plus
	statValue[1] = int32(statValue[0] * reforge->newstat_coef); // new stat value: apply:plus  unapply:minus

	statType[0] = reforge->oldstat;
	statType[1] = reforge->newstat;

	sLog->outDebug(
			LOG_FILTER_PLAYER_ITEMS,
			"ApplyItemReforge : item %u, reforge id %u, stat value %d, old stat %u (x%.1f %d), new stat %u (x%.1f %d), apply %d",
			item->GetEntry(), reforge_id, statBaseValue, reforge->oldstat,
			reforge->oldstat_coef, statValue[0], reforge->newstat,
			reforge->newstat_coef, statValue[1], apply);

	for (int32 i = 0; i < 2; i++) {
		switch (statType[i]) {
		case ITEM_MOD_SPIRIT:
			sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u SPIRIT",
					statValue[i]);
			HandleStatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE,
					float(statValue[i]), (i == 0) ? (!apply) : apply);
			ApplyStatBuffMod(STAT_SPIRIT, (float) statValue[i],
					(i == 0) ? (!apply) : apply);
		case ITEM_MOD_DODGE_RATING:
			ApplyRatingMod(CR_DODGE, statValue[i], (i == 0) ? (!apply) : apply);
			sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u DODGE", statValue[i]);
			break;
		case ITEM_MOD_PARRY_RATING:
			ApplyRatingMod(CR_PARRY, statValue[i], (i == 0) ? (!apply) : apply);
			sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u PARRY", statValue[i]);
			break;
		case ITEM_MOD_HIT_RATING:
			ApplyRatingMod(CR_HIT_MELEE, statValue[i],
					(i == 0) ? (!apply) : apply);
			ApplyRatingMod(CR_HIT_RANGED, statValue[i],
					(i == 0) ? (!apply) : apply);
			ApplyRatingMod(CR_HIT_SPELL, statValue[i],
					(i == 0) ? (!apply) : apply);
			sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u HIT", statValue[i]);
			break;
		case ITEM_MOD_CRIT_RATING:
			ApplyRatingMod(CR_CRIT_MELEE, statValue[i],
					(i == 0) ? (!apply) : apply);
			ApplyRatingMod(CR_CRIT_RANGED, statValue[i],
					(i == 0) ? (!apply) : apply);
			ApplyRatingMod(CR_CRIT_SPELL, statValue[i],
					(i == 0) ? (!apply) : apply);
			sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u CRITICAL",
					statValue[i]);
			break;
		case ITEM_MOD_HASTE_RATING:
			ApplyRatingMod(CR_HASTE_MELEE, statValue[i],
					(i == 0) ? (!apply) : apply);
			ApplyRatingMod(CR_HASTE_RANGED, statValue[i],
					(i == 0) ? (!apply) : apply);
			ApplyRatingMod(CR_HASTE_SPELL, statValue[i],
					(i == 0) ? (!apply) : apply);
			sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u HASTE", statValue[i]);
			break;
		case ITEM_MOD_EXPERTISE_RATING:
			ApplyRatingMod(CR_EXPERTISE, statValue[i],
					(i == 0) ? (!apply) : apply);
			sLog->outDebug(LOG_FILTER_PLAYER_ITEMS, "+ %u EXPERTISE",
					statValue[i]);
			break;
		case ITEM_MOD_MASTERY_RATING:
			ApplyRatingMod(CR_MASTERY, int32(statValue[i]),
					(i == 0) ? (!apply) : apply);
			break;
		default:
			break;
		}
	}
}

void Player::UpdateSkillEnchantments(uint16 skill_id, uint16 curr_value,
		uint16 new_value) {
	for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; ++i) {
		if (m_items[i]) {
			for (uint8 slot = 0; slot < MAX_ENCHANTMENT_SLOT; ++slot) {
				uint32 ench_id = m_items[i]->GetEnchantmentId(
						EnchantmentSlot(slot));
				if (!ench_id)
					continue;

				SpellItemEnchantmentEntry const *Enchant =
						sSpellItemEnchantmentStore.LookupEntry(ench_id);
				if (!Enchant)
					return;

				if (Enchant->requiredSkill == skill_id) {
					// Checks if the enchantment needs to be applied or removed
					if (curr_value < Enchant->requiredSkillValue
							&& new_value >= Enchant->requiredSkillValue)
						ApplyEnchantment(m_items[i], EnchantmentSlot(slot),
								true);
					else if (new_value < Enchant->requiredSkillValue
							&& curr_value >= Enchant->requiredSkillValue)
						ApplyEnchantment(m_items[i], EnchantmentSlot(slot),
								false);
				}

				// If we're dealing with a gem inside a prismatic socket we need to check the prismatic socket requirements
				// rather than the gem requirements itself. If the socket has no color it is a prismatic socket.
				if ((slot == SOCK_ENCHANTMENT_SLOT
						|| slot == SOCK_ENCHANTMENT_SLOT_2
						|| slot == SOCK_ENCHANTMENT_SLOT_3)
						&& !m_items[i]->GetProto()->Socket[slot
								- SOCK_ENCHANTMENT_SLOT].Color) {
					SpellItemEnchantmentEntry const *pPrismaticEnchant =
							sSpellItemEnchantmentStore.LookupEntry(
									m_items[i]->GetEnchantmentId(
											PRISMATIC_ENCHANTMENT_SLOT));

					if (pPrismaticEnchant
							&& pPrismaticEnchant->requiredSkill == skill_id) {
						if (curr_value < pPrismaticEnchant->requiredSkillValue
								&& new_value
										>= pPrismaticEnchant->requiredSkillValue)
							ApplyEnchantment(m_items[i], EnchantmentSlot(slot),
									true);
						else if (new_value
								< pPrismaticEnchant->requiredSkillValue
								&& curr_value
										>= pPrismaticEnchant->requiredSkillValue)
							ApplyEnchantment(m_items[i], EnchantmentSlot(slot),
									false);
					}
				}
			}
		}
	}
}

void Player::SendEnchantmentDurations() {
	for (EnchantDurationList::const_iterator itr = m_enchantDuration.begin();
			itr != m_enchantDuration.end(); ++itr) {
		GetSession()->SendItemEnchantTimeUpdate(GetGUID(), itr->item->GetGUID(),
				itr->slot, uint32(itr->leftduration) / 1000);
	}
}

void Player::SendItemDurations() {
	for (ItemDurationList::const_iterator itr = m_itemDuration.begin();
			itr != m_itemDuration.end(); ++itr) {
		(*itr)->SendTimeUpdate(this);
	}
}

void Player::SendNewItem(Item *item, uint32 count, bool received, bool created,
		bool broadcast) {
	if (!item) // prevent crash
		return;

	// last check 2.0.10
	WorldPacket data(SMSG_ITEM_PUSH_RESULT,
			(8 + 4 + 4 + 4 + 1 + 4 + 4 + 4 + 4 + 4));
	data << uint64(GetGUID()); // player GUID
	data << uint32(received); // 0=looted, 1=from npc
	data << uint32(created); // 0=received, 1=created
	data << uint32(1); // always 0x01 (probably meant to be count of listed items)
	data << uint8(item->GetBagSlot()); // bagslot
									   // item slot, but when added to stack: 0xFFFFFFFF
	data << uint32((item->GetCount() == count) ? item->GetSlot() : -1);
	data << uint32(item->GetEntry()); // item id
	data << uint32(item->GetItemSuffixFactor()); // SuffixFactor
	data << int32(item->GetItemRandomPropertyId()); // random item property id
	data << uint32(count); // count of items
	data << uint32(GetItemCount(item->GetEntry())); // count of items in inventory

	if (broadcast && GetGroup())
		GetGroup()->BroadcastPacket(&data, true);
	else
		GetSession()->SendPacket(&data);
}

/*********************************************************/
/***                    GOSSIP SYSTEM                  ***/
/*********************************************************/

void Player::PrepareGossipMenu(WorldObject *pSource, uint32 menuId,
		bool showQuests) {
	PlayerMenu* pMenu = PlayerTalkClass;
	pMenu->ClearMenus();

	pMenu->GetGossipMenu().SetMenuId(menuId);

	GossipMenuItemsMapBounds pMenuItemBounds =
			sObjectMgr->GetGossipMenuItemsMapBounds(menuId);

	// if default menuId and no menu options exist for this, use options from default options
	if (pMenuItemBounds.first == pMenuItemBounds.second
			&& menuId == GetDefaultGossipMenuForSource(pSource))
		pMenuItemBounds = sObjectMgr->GetGossipMenuItemsMapBounds(0);

	uint32 npcflags = 0;
	Creature *pCreature = NULL;

	if (pSource->GetTypeId() == TYPEID_UNIT) {
		pCreature = pSource->ToCreature();
		npcflags = pCreature->GetUInt32Value(UNIT_NPC_FLAGS);
		if (npcflags & UNIT_NPC_FLAG_QUESTGIVER && showQuests)
			PrepareQuestMenu(pSource->GetGUID());
	}

	for (GossipMenuItemsMap::const_iterator itr = pMenuItemBounds.first;
			itr != pMenuItemBounds.second; ++itr) {
		bool bCanTalk = true;
		if (!sConditionMgr->IsPlayerMeetToConditions(this,
				itr->second.conditions))
			continue;

		if (pSource->GetTypeId() == TYPEID_UNIT) {
			if (!(itr->second.npc_option_npcflag & npcflags))
				continue;

			switch (itr->second.option_id) {
			case GOSSIP_OPTION_ARMORER:
				bCanTalk = false; // added in special mode
				break;
			case GOSSIP_OPTION_SPIRITHEALER:
				if (!isDead())
					bCanTalk = false;
				break;
			case GOSSIP_OPTION_VENDOR: {
				VendorItemData const* vItems = pCreature->GetVendorItems();
				if (!vItems || vItems->Empty()) {
					sLog->outErrorDb(
							"Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.",
							pCreature->GetGUIDLow(), pCreature->GetEntry());
					bCanTalk = false;
				}
				break;
			}
			case GOSSIP_OPTION_TRAINER:
				if (!pCreature->isCanTrainingOf(this, false))
					bCanTalk = false;
				break;
			case GOSSIP_OPTION_LEARNDUALSPEC:
				if (!(GetSpecsCount() == 1
						&& pCreature->isCanTrainingAndResetTalentsOf(this)
						&& !(getLevel()
								< sWorld->getIntConfig(
										CONFIG_MIN_DUALSPEC_LEVEL))))
					bCanTalk = false;
				break;
			case GOSSIP_OPTION_UNLEARNTALENTS:
				if (!pCreature->isCanTrainingAndResetTalentsOf(this))
					bCanTalk = false;
				break;
			case GOSSIP_OPTION_UNLEARNPETTALENTS:
				if (!GetPet() || GetPet()->getPetType() != HUNTER_PET
						|| GetPet()->m_spells.size() <= 1
						|| pCreature->GetCreatureInfo()->trainer_type
								!= TRAINER_TYPE_PETS
						|| pCreature->GetCreatureInfo()->trainer_class
								!= CLASS_HUNTER)
					bCanTalk = false;
				break;
			case GOSSIP_OPTION_TAXIVENDOR:
				if (GetSession()->SendLearnNewTaxiNode(pCreature))
					return;
				break;
			case GOSSIP_OPTION_BATTLEFIELD:
				if (!pCreature->isCanInteractWithBattleMaster(this, false))
					bCanTalk = false;
				break;
			case GOSSIP_OPTION_STABLEPET:
				if (getClass() != CLASS_HUNTER)
					bCanTalk = false;
				break;
			case GOSSIP_OPTION_QUESTGIVER:
				bCanTalk = false;
				break;
			case GOSSIP_OPTION_GOSSIP:
			case GOSSIP_OPTION_SPIRITGUIDE:
			case GOSSIP_OPTION_INNKEEPER:
			case GOSSIP_OPTION_BANKER:
			case GOSSIP_OPTION_PETITIONER:
			case GOSSIP_OPTION_TABARDDESIGNER:
			case GOSSIP_OPTION_AUCTIONEER:
			case GOSSIP_OPTION_REFORGE:
				break; // no checks
			case GOSSIP_OPTION_OUTDOORPVP:
				if (!sOutdoorPvPMgr->CanTalkTo(this, pCreature, itr->second))
					bCanTalk = false;
				break;
			default:
				sLog->outErrorDb(
						"Creature entry %u have unknown gossip option %u for menu %u",
						pCreature->GetEntry(), itr->second.option_id,
						itr->second.menu_id);
				bCanTalk = false;
				break;
			}
		} else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT) {
			GameObject *pGo = (GameObject*) pSource;

			switch (itr->second.option_id) {
			case GOSSIP_OPTION_QUESTGIVER:
				if (pGo->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER)
					PrepareQuestMenu(pSource->GetGUID());
				bCanTalk = false;
				break;
			case GOSSIP_OPTION_GOSSIP:
				if (pGo->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER
						&& pGo->GetGoType() != GAMEOBJECT_TYPE_GOOBER)
					bCanTalk = false;
				break;
			default:
				bCanTalk = false;
				break;
			}
		}

		if (bCanTalk) {
			std::string strOptionText = itr->second.option_text;
			std::string strBoxText = itr->second.box_text;

			int loc_idx = GetSession()->GetSessionDbLocaleIndex();
			if (loc_idx >= 0) {
				uint32 idxEntry = MAKE_PAIR32(menuId, itr->second.id);
				if (GossipMenuItemsLocale const *no = sObjectMgr->GetGossipMenuItemsLocale(idxEntry)) {
					sObjectMgr->GetLocaleString(no->OptionText, loc_idx,
							strOptionText);
					sObjectMgr->GetLocaleString(no->BoxText, loc_idx,
							strBoxText);
				}
			}

			pMenu->GetGossipMenu().AddMenuItem(itr->second.option_icon,
					strOptionText, 0, itr->second.option_id, strBoxText,
					itr->second.box_money, itr->second.box_coded);
			pMenu->GetGossipMenu().AddGossipMenuItemData(
					itr->second.action_menu_id, itr->second.action_poi_id,
					itr->second.action_script_id);
		}
	}
}

void Player::SendPreparedGossip(WorldObject *pSource) {
	if (!pSource)
		return;

	if (pSource->GetTypeId() == TYPEID_UNIT) {
		// in case no gossip flag and quest menu not empty, open quest menu (client expect gossip menu with this flag)
		if (!pSource->ToCreature()->HasFlag(UNIT_NPC_FLAGS,
				UNIT_NPC_FLAG_GOSSIP)
				&& !PlayerTalkClass->GetQuestMenu().Empty()) {
			SendPreparedQuest(pSource->GetGUID());
			return;
		}
	} else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT) {
		// probably need to find a better way here
		if (!PlayerTalkClass->GetGossipMenu().GetMenuId()
				&& !PlayerTalkClass->GetQuestMenu().Empty()) {
			SendPreparedQuest(pSource->GetGUID());
			return;
		}
	}

	// in case non empty gossip menu (that not included quests list size) show it
	// (quest entries from quest menu will be included in list)

	uint32 textId = GetGossipTextId(pSource);

	if (uint32 menuId = PlayerTalkClass->GetGossipMenu().GetMenuId())
		textId = GetGossipTextId(menuId);

	PlayerTalkClass->SendGossipMenu(textId, pSource->GetGUID());
}

void Player::OnGossipSelect(WorldObject* pSource, uint32 gossipListId,
		uint32 menuId) {
	GossipMenu& gossipmenu = PlayerTalkClass->GetGossipMenu();

	if (gossipListId >= gossipmenu.MenuItemCount())
		return;

	// if not same, then something funky is going on
	if (menuId != gossipmenu.GetMenuId())
		return;

	uint32 gossipOptionId = gossipmenu.GetItem(gossipListId).m_gOptionId;
	uint64 guid = pSource->GetGUID();

	if (pSource->GetTypeId() == TYPEID_GAMEOBJECT) {
		if (gossipOptionId > GOSSIP_OPTION_QUESTGIVER) {
			sLog->outError(
					"Player guid %u request invalid gossip option for GameObject entry %u",
					GetGUIDLow(), pSource->GetEntry());
			return;
		}
	}

	GossipMenuItemData pMenuData = gossipmenu.GetItemData(gossipListId);

	uint32 cost = gossipmenu.GetItem(gossipListId).m_gBoxMoney;
	if (!HasEnoughMoney(cost)) {
		SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
		PlayerTalkClass->CloseGossip();
		return;
	}

	switch (gossipOptionId) {
	case GOSSIP_OPTION_GOSSIP: {
		if (pMenuData.m_gAction_menu) {
			PrepareGossipMenu(pSource, pMenuData.m_gAction_menu);
			SendPreparedGossip(pSource);
		}

		if (pMenuData.m_gAction_poi)
			PlayerTalkClass->SendPointOfInterest(pMenuData.m_gAction_poi);

		if (pMenuData.m_gAction_script) {
			if (pSource->GetTypeId() == TYPEID_UNIT)
				GetMap()->ScriptsStart(sGossipScripts,
						pMenuData.m_gAction_script, this, pSource);
			else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT)
				GetMap()->ScriptsStart(sGossipScripts,
						pMenuData.m_gAction_script, pSource, this);
		}
		break;
	}
	case GOSSIP_OPTION_OUTDOORPVP:
		sOutdoorPvPMgr->HandleGossipOption(this, pSource->GetGUID(),
				gossipListId);
		break;
	case GOSSIP_OPTION_SPIRITHEALER:
		if (isDead())
			pSource->ToCreature()->CastSpell((pSource->ToCreature()), 17251,
					true, NULL, NULL, GetGUID());
		break;
	case GOSSIP_OPTION_QUESTGIVER:
		PrepareQuestMenu(guid);
		SendPreparedQuest(guid);
		break;
	case GOSSIP_OPTION_VENDOR:
	case GOSSIP_OPTION_ARMORER:
		GetSession()->SendListInventory(guid);
		break;
	case GOSSIP_OPTION_STABLEPET:
		GetSession()->SendStablePet(guid);
		break;
	case GOSSIP_OPTION_TRAINER:
		GetSession()->SendTrainerList(guid);
		break;
	case GOSSIP_OPTION_LEARNDUALSPEC:
		if (GetSpecsCount() == 1
				&& getLevel() >= sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL)) {
			// Cast spells that teach dual spec
			// Both are also ImplicitTarget self and must be cast by player
			CastSpell(this, 63680, true, NULL, NULL, GetGUID());
			CastSpell(this, 63624, true, NULL, NULL, GetGUID());

			// Should show another Gossip text with "Congratulations..."
			PlayerTalkClass->CloseGossip();
		}
		break;
	case GOSSIP_OPTION_UNLEARNTALENTS:
		PlayerTalkClass->CloseGossip();
		pSource->ToCreature()->CastSpell(this, 46331, true); // Trainer: Untrain Talents
		break;
	case GOSSIP_OPTION_UNLEARNPETTALENTS:
		PlayerTalkClass->CloseGossip();
		ResetPetTalents();
		break;
	case GOSSIP_OPTION_TAXIVENDOR:
		GetSession()->SendTaxiMenu((pSource->ToCreature()));
		break;
	case GOSSIP_OPTION_INNKEEPER:
		PlayerTalkClass->CloseGossip();
		SetBindPoint(guid);
		break;
	case GOSSIP_OPTION_BANKER:
		GetSession()->SendShowBank(guid);
		break;
	case GOSSIP_OPTION_PETITIONER:
		PlayerTalkClass->CloseGossip();
		GetSession()->SendPetitionShowList(guid);
		break;
	case GOSSIP_OPTION_TABARDDESIGNER:
		PlayerTalkClass->CloseGossip();
		GetSession()->SendTabardVendorActivate(guid);
		break;
	case GOSSIP_OPTION_AUCTIONEER:
		GetSession()->SendAuctionHello(guid, (pSource->ToCreature()));
		break;
	case GOSSIP_OPTION_SPIRITGUIDE:
		PrepareGossipMenu(pSource);
		SendPreparedGossip(pSource);
		break;
	case GOSSIP_OPTION_BATTLEFIELD: {
		if (pSource->GetTypeId() != TYPEID_UNIT
				|| !pSource->ToCreature()->isBattleMaster()) {
			sLog->outError(
					"a user (guid %u) requested battlegroundlist from a npc who is no battlemaster",
					GetGUIDLow());
			return;
		}

		GetSession()->SendBattlegGroundList(guid);
		break;
	}
	case GOSSIP_OPTION_REFORGE:
		GetSession()->SendShowReforge(guid);
		break;
	}

	ModifyMoney(-int32(cost));
}

uint32 Player::GetGossipTextId(WorldObject *pSource) {
	if (!pSource || pSource->GetTypeId() != TYPEID_UNIT
			|| !pSource->ToCreature()->GetDBTableGUIDLow())
		return DEFAULT_GOSSIP_MESSAGE;

	if (uint32 pos = sObjectMgr->GetNpcGossip(pSource->ToCreature()->GetDBTableGUIDLow()))
		return pos;

	return DEFAULT_GOSSIP_MESSAGE;
}

uint32 Player::GetGossipTextId(uint32 menuId) {
	uint32 textId = DEFAULT_GOSSIP_MESSAGE;

	if (!menuId)
		return textId;

	GossipMenusMapBounds pMenuBounds = sObjectMgr->GetGossipMenusMapBounds(
			menuId);

	for (GossipMenusMap::const_iterator itr = pMenuBounds.first;
			itr != pMenuBounds.second; ++itr) {
		if (sConditionMgr->IsPlayerMeetToConditions(this,
				itr->second.conditions))
			textId = itr->second.text_id;
	}

	return textId;
}

uint32 Player::GetDefaultGossipMenuForSource(WorldObject *pSource) {
	if (pSource->GetTypeId() == TYPEID_UNIT)
		return pSource->ToCreature()->GetCreatureInfo()->GossipMenuId;
	else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT)
		return ((GameObject*) pSource)->GetGOInfo()->GetGossipMenuId();

	return 0;
}

/*********************************************************/
/***                    QUEST SYSTEM                   ***/
/*********************************************************/

void Player::PrepareQuestMenu(uint64 guid) {
	QuestRelationBounds pObjectQR;
	QuestRelationBounds pObjectQIR;

	// pets also can have quests
	Creature *pCreature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this,
			guid);
	if (pCreature) {
		pObjectQR = sObjectMgr->GetCreatureQuestRelationBounds(
				pCreature->GetEntry());
		pObjectQIR = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(
				pCreature->GetEntry());
	} else {
		//we should obtain map pointer from GetMap() in 99% of cases. Special case
		//only for quests which cast teleport spells on player
		Map * _map =
				IsInWorld() ?
						GetMap() :
						sMapMgr->FindMap(GetMapId(), GetInstanceId());
		ASSERT(_map);
		GameObject *pGameObject = _map->GetGameObject(guid);
		if (pGameObject) {
			pObjectQR = sObjectMgr->GetGOQuestRelationBounds(
					pGameObject->GetEntry());
			pObjectQIR = sObjectMgr->GetGOQuestInvolvedRelationBounds(
					pGameObject->GetEntry());
		} else
			return;
	}

	QuestMenu &qm = PlayerTalkClass->GetQuestMenu();
	qm.ClearMenu();

	for (QuestRelations::const_iterator i = pObjectQIR.first;
			i != pObjectQIR.second; ++i) {
		uint32 quest_id = i->second;
		QuestStatus status = GetQuestStatus(quest_id);
		if (status == QUEST_STATUS_COMPLETE)
			qm.AddMenuItem(quest_id, 4);
		else if (status == QUEST_STATUS_INCOMPLETE)
			qm.AddMenuItem(quest_id, 4);
		//else if (status == QUEST_STATUS_AVAILABLE)
		//    qm.AddMenuItem(quest_id, 2);
	}

	for (QuestRelations::const_iterator i = pObjectQR.first;
			i != pObjectQR.second; ++i) {
		uint32 quest_id = i->second;
		Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest_id);
		if (!pQuest)
			continue;

		if (!CanTakeQuest(pQuest, false))
			continue;

		if (pQuest->IsAutoComplete())
			qm.AddMenuItem(quest_id, 4);
		else if (GetQuestStatus(quest_id) == QUEST_STATUS_NONE)
			qm.AddMenuItem(quest_id, 2);
	}
}

void Player::SendPreparedQuest(uint64 guid) {
	QuestMenu& questMenu = PlayerTalkClass->GetQuestMenu();
	if (questMenu.Empty())
		return;

	QuestMenuItem const& qmi0 = questMenu.GetItem(0);

	uint32 icon = qmi0.m_qIcon;

	// single element case
	if (questMenu.MenuItemCount() == 1) {
		// Auto open -- maybe also should verify there is no greeting
		uint32 quest_id = qmi0.m_qId;
		Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest_id);

		if (pQuest) {
			if (icon == 4 && !GetQuestRewardStatus(quest_id))
				PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid,
						CanRewardQuest(pQuest, false), true);
			else if (icon == 4)
				PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid,
						CanRewardQuest(pQuest, false), true);
			// Send completable on repeatable and autoCompletable quest if player don't have quest
			// TODO: verify if check for !pQuest->IsDaily() is really correct (possibly not)
			else {
				Object* pObject = ObjectAccessor::GetObjectByTypeMask(*this,
						guid,
						TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_ITEM);
				if (!pObject
						|| (!pObject->hasQuest(quest_id)
								&& !pObject->hasInvolvedQuest(quest_id))) {
					PlayerTalkClass->CloseGossip();
					return;
				}

				if (pQuest->IsAutoAccept() && CanAddQuest(pQuest, true)) {
					AddQuest(pQuest, pObject);
					if (CanCompleteQuest(quest_id))
						CompleteQuest(quest_id);
				}

				if ((pQuest->IsAutoComplete() && pQuest->IsRepeatable()
						&& !pQuest->IsDailyOrWeekly())
						|| pQuest->HasFlag(QUEST_FLAGS_AUTOCOMPLETE))
					PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid,
							CanCompleteRepeatableQuest(pQuest), true);
				else
					PlayerTalkClass->SendQuestGiverQuestDetails(pQuest, guid,
							true);
			}
		}
	}
	// multiple entries
	else {
		QEmote qe;
		qe._Delay = 0;
		qe._Emote = 0;
		std::string title = "";

		// need pet case for some quests
		Creature *pCreature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this,
				guid);
		if (pCreature) {
			uint32 textid = GetGossipTextId(pCreature);
			GossipText const* gossiptext = sObjectMgr->GetGossipText(textid);
			if (!gossiptext) {
				qe._Delay = 0; //TEXTEMOTE_MESSAGE;              //zyg: player emote
				qe._Emote = 0; //TEXTEMOTE_HELLO;                //zyg: NPC emote
				title = "";
			} else {
				qe = gossiptext->Options[0].Emotes[0];

				if (!gossiptext->Options[0].Text_0.empty()) {
					title = gossiptext->Options[0].Text_0;

					int loc_idx = GetSession()->GetSessionDbLocaleIndex();
					if (loc_idx >= 0)
						if (NpcTextLocale const *nl = sObjectMgr->GetNpcTextLocale(textid))
							sObjectMgr->GetLocaleString(nl->Text_0[0], loc_idx,
									title);
				} else {
					title = gossiptext->Options[0].Text_1;

					int loc_idx = GetSession()->GetSessionDbLocaleIndex();
					if (loc_idx >= 0)
						if (NpcTextLocale const *nl = sObjectMgr->GetNpcTextLocale(textid))
							sObjectMgr->GetLocaleString(nl->Text_1[0], loc_idx,
									title);
				}
			}
		}
		PlayerTalkClass->SendQuestGiverQuestList(qe, title, guid);
	}
}

bool Player::IsActiveQuest(uint32 quest_id) const {
	return m_QuestStatus.find(quest_id) != m_QuestStatus.end();
}

Quest const * Player::GetNextQuest(uint64 guid, Quest const *pQuest) {
	QuestRelationBounds pObjectQR;

	Creature *pCreature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this,
			guid);
	if (pCreature)
		pObjectQR = sObjectMgr->GetCreatureQuestRelationBounds(
				pCreature->GetEntry());
	else {
		//we should obtain map pointer from GetMap() in 99% of cases. Special case
		//only for quests which cast teleport spells on player
		Map * _map =
				IsInWorld() ?
						GetMap() :
						sMapMgr->FindMap(GetMapId(), GetInstanceId());
		ASSERT(_map);
		GameObject *pGameObject = _map->GetGameObject(guid);
		if (pGameObject)
			pObjectQR = sObjectMgr->GetGOQuestRelationBounds(
					pGameObject->GetEntry());
		else
			return NULL;
	}

	uint32 nextQuestID = pQuest->GetNextQuestInChain();
	for (QuestRelations::const_iterator itr = pObjectQR.first;
			itr != pObjectQR.second; ++itr) {
		if (itr->second == nextQuestID)
			return sObjectMgr->GetQuestTemplate(nextQuestID);
	}

	return NULL;
}

bool Player::CanSeeStartQuest(Quest const *pQuest) {
	if (SatisfyQuestRace(pQuest, false)
			&& SatisfyQuestSkillOrClass(pQuest, false)
			&& SatisfyQuestExclusiveGroup(pQuest, false)
			&& SatisfyQuestReputation(pQuest, false)
			&& SatisfyQuestPreviousQuest(pQuest, false)
			&& SatisfyQuestNextChain(pQuest, false)
			&& SatisfyQuestPrevChain(pQuest, false)
			&& SatisfyQuestDay(pQuest, false) && SatisfyQuestWeek(pQuest, false)
			&& !sDisableMgr->IsDisabledFor(DISABLE_TYPE_QUEST,
					pQuest->GetQuestId(), this)) {
		return getLevel()
				+ sWorld->getIntConfig(CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF)
				>= pQuest->GetMinLevel();
	}

	return false;
}

bool Player::CanTakeQuest(Quest const *pQuest, bool msg) {
	return SatisfyQuestStatus(pQuest, msg)
			&& SatisfyQuestExclusiveGroup(pQuest, msg)
			&& SatisfyQuestRace(pQuest, msg) && SatisfyQuestLevel(pQuest, msg)
			&& SatisfyQuestSkillOrClass(pQuest, msg)
			&& SatisfyQuestReputation(pQuest, msg)
			&& SatisfyQuestPreviousQuest(pQuest, msg)
			&& SatisfyQuestTimed(pQuest, msg)
			&& SatisfyQuestNextChain(pQuest, msg)
			&& SatisfyQuestPrevChain(pQuest, msg)
			&& SatisfyQuestDay(pQuest, msg) && SatisfyQuestWeek(pQuest, msg)
			&& !sDisableMgr->IsDisabledFor(DISABLE_TYPE_QUEST,
					pQuest->GetQuestId(), this)
			&& SatisfyQuestConditions(pQuest, msg);
}

bool Player::CanAddQuest(Quest const *pQuest, bool msg) {
	if (!SatisfyQuestLog(msg))
		return false;

	uint32 srcitem = pQuest->GetSrcItemId();
	if (srcitem > 0) {
		uint32 count = pQuest->GetSrcItemCount();
		ItemPosCountVec dest;
		uint8 msg2 = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, srcitem, count);

		// player already have max number (in most case 1) source item, no additional item needed and quest can be added.
		if (msg2 == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS)
			return true;
		else if (msg2 != EQUIP_ERR_OK) {
			SendEquipError(msg2, NULL, NULL, srcitem);
			return false;
		}
	}
	return true;
}

bool Player::CanCompleteQuest(uint32 quest_id) {
	if (quest_id) {
		Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id);
		if (!qInfo)
			return false;

		RewardedQuestSet::iterator rewItr = m_RewardedQuests.find(quest_id);
		if (!qInfo->IsRepeatable() && rewItr != m_RewardedQuests.end())
			return false; // not allow re-complete quest

		// auto complete quest
		if ((qInfo->IsAutoComplete()
				|| qInfo->GetQuestFlags() & QUEST_FLAGS_AUTOCOMPLETE)
				&& CanTakeQuest(qInfo, false))
			return true;

		QuestStatusMap::iterator itr = m_QuestStatus.find(quest_id);
		if (itr == m_QuestStatus.end())
			return false;

		QuestStatusData &q_status = itr->second;

		if (q_status.m_status == QUEST_STATUS_INCOMPLETE) {
			if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER)) {
				for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++) {
					if (qInfo->ReqItemCount[i] != 0
							&& q_status.m_itemcount[i] < qInfo->ReqItemCount[i])
						return false;
				}
			}

			if (qInfo->HasSpecialFlag(
					QUEST_SPECIAL_FLAG_KILL_OR_CAST
							| QUEST_SPECIAL_FLAG_SPEAKTO)) {
				for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; i++) {
					if (qInfo->ReqCreatureOrGOId[i] == 0)
						continue;

					if (qInfo->ReqCreatureOrGOCount[i] != 0
							&& q_status.m_creatureOrGOcount[i]
									< qInfo->ReqCreatureOrGOCount[i])
						return false;
				}
			}

			if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT)
					&& !q_status.m_explored)
				return false;

			if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_TIMED)
					&& q_status.m_timer == 0)
				return false;

			if (qInfo->GetRewOrReqMoney() < 0) {
				if (!HasEnoughMoney(-qInfo->GetRewOrReqMoney()))
					return false;
			}

			uint32 repFacId = qInfo->GetRepObjectiveFaction();
			if (repFacId
					&& GetReputationMgr().GetReputation(repFacId)
							< qInfo->GetRepObjectiveValue())
				return false;

			uint32 repFacId2 = qInfo->GetRepObjectiveFaction2();
			if (repFacId2
					&& GetReputationMgr().GetReputation(repFacId)
							< qInfo->GetRepObjectiveValue2())
				return false;

			return true;
		}
	}
	return false;
}

bool Player::CanCompleteRepeatableQuest(Quest const *pQuest) {
	// Solve problem that player don't have the quest and try complete it.
	// if repeatable she must be able to complete event if player don't have it.
	// Seem that all repeatable quest are DELIVER Flag so, no need to add more.
	if (!CanTakeQuest(pQuest, false))
		return false;

	if (pQuest->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER))
		for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++)
			if (pQuest->ReqItemId[i] && pQuest->ReqItemCount[i]
					&& !HasItemCount(pQuest->ReqItemId[i],
							pQuest->ReqItemCount[i]))
				return false;

	if (!CanRewardQuest(pQuest, false))
		return false;

	return true;
}

bool Player::CanRewardQuest(Quest const *pQuest, bool msg) {
	// not auto complete quest and not completed quest (only cheating case, then ignore without message)
	if (!pQuest->IsDFQuest() && !pQuest->IsAutoComplete()
			&& !(pQuest->GetQuestFlags() & QUEST_FLAGS_AUTOCOMPLETE)
			&& GetQuestStatus(pQuest->GetQuestId()) != QUEST_STATUS_COMPLETE)
		return false;

	// daily quest can't be rewarded (25 daily quest already completed)
	if (!SatisfyQuestDay(pQuest, true) || !SatisfyQuestWeek(pQuest, true))
		return false;

	// rewarded and not repeatable quest (only cheating case, then ignore without message)
	if (GetQuestRewardStatus(pQuest->GetQuestId()))
		return false;

	// prevent receive reward with quest items in bank
	if (pQuest->HasFlag(QUEST_SPECIAL_FLAG_DELIVER)) {
		for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++) {
			if (pQuest->ReqItemCount[i] != 0
					&& GetItemCount(pQuest->ReqItemId[i])
							< pQuest->ReqItemCount[i]) {
				if (msg)
					SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL,
							pQuest->ReqItemId[i]);
				return false;
			}
		}
	}

	// prevent receive reward with low money and GetRewOrReqMoney() < 0
	if (pQuest->GetRewOrReqMoney() < 0
			&& !HasEnoughMoney(-pQuest->GetRewOrReqMoney()))
		return false;

	return true;
}

bool Player::CanRewardQuest(Quest const *pQuest, uint32 reward, bool msg) {
	// prevent receive reward with quest items in bank or for not completed quest
	if (!CanRewardQuest(pQuest, msg))
		return false;

	if (pQuest->GetRewChoiceItemsCount() > 0) {
		if (pQuest->RewChoiceItemId[reward]) {
			ItemPosCountVec dest;
			uint8 res = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest,
					pQuest->RewChoiceItemId[reward],
					pQuest->RewChoiceItemCount[reward]);
			if (res != EQUIP_ERR_OK) {
				SendEquipError(res, NULL, NULL,
						pQuest->RewChoiceItemId[reward]);
				return false;
			}
		}
	}

	if (pQuest->GetRewItemsCount() > 0) {
		for (uint32 i = 0; i < pQuest->GetRewItemsCount(); ++i) {
			if (pQuest->RewItemId[i]) {
				ItemPosCountVec dest;
				uint8 res = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest,
						pQuest->RewItemId[i], pQuest->RewItemCount[i]);
				if (res != EQUIP_ERR_OK) {
					SendEquipError(res, NULL, NULL, pQuest->RewItemId[i]);
					return false;
				}
			}
		}
	}

	return true;
}

void Player::AddQuest(Quest const *pQuest, Object *questGiver) {
	uint16 log_slot = FindQuestSlot(0);
	ASSERT(log_slot < MAX_QUEST_LOG_SIZE);

	uint32 quest_id = pQuest->GetQuestId();

	// if not exist then created with set uState == NEW and rewarded=false
	QuestStatusData& questStatusData = m_QuestStatus[quest_id];

	// check for repeatable quests status reset
	questStatusData.m_status = QUEST_STATUS_INCOMPLETE;
	questStatusData.m_explored = false;

	if (pQuest->HasFlag(QUEST_SPECIAL_FLAG_DELIVER)) {
		for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
			questStatusData.m_itemcount[i] = 0;
	}

	if (pQuest->HasFlag(
			QUEST_SPECIAL_FLAG_KILL_OR_CAST | QUEST_SPECIAL_FLAG_SPEAKTO)) {
		for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
			questStatusData.m_creatureOrGOcount[i] = 0;
	}

	GiveQuestSourceItem(pQuest);
	AdjustQuestReqItemCount(pQuest, questStatusData);

	if (pQuest->GetRepObjectiveFaction())
		if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(pQuest->GetRepObjectiveFaction()))
			GetReputationMgr().SetVisible(factionEntry);

	if (pQuest->GetRepObjectiveFaction2())
		if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(pQuest->GetRepObjectiveFaction2()))
			GetReputationMgr().SetVisible(factionEntry);

	uint32 qtime = 0;
	if (pQuest->HasFlag(QUEST_SPECIAL_FLAG_TIMED)) {
		uint32 limittime = pQuest->GetLimitTime();

		// shared timed quest
		if (questGiver && questGiver->GetTypeId() == TYPEID_PLAYER)
			limittime =
					questGiver->ToPlayer()->getQuestStatusMap()[quest_id].m_timer
							/ IN_MILLISECONDS;

		AddTimedQuest(quest_id);
		questStatusData.m_timer = limittime * IN_MILLISECONDS;
		qtime = static_cast<uint32>(time(NULL)) + limittime;
	} else
		questStatusData.m_timer = 0;

	SetQuestSlot(log_slot, quest_id, qtime);

	m_QuestStatusSave[quest_id] = true;

	GetAchievementMgr().StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST,
			quest_id);

	//starting initial quest script
	if (questGiver && pQuest->GetQuestStartScript() != 0)
		GetMap()->ScriptsStart(sQuestStartScripts,
				pQuest->GetQuestStartScript(), questGiver, this);

	// Some spells applied at quest activation
	SpellAreaForQuestMapBounds saBounds =
			sSpellMgr->GetSpellAreaForQuestMapBounds(quest_id, true);
	if (saBounds.first != saBounds.second) {
		uint32 zone, area;
		GetZoneAndAreaId(zone, area);

		for (SpellAreaForAreaMap::const_iterator itr = saBounds.first;
				itr != saBounds.second; ++itr)
			if (itr->second->autocast
					&& itr->second->IsFitToRequirements(this, zone, area))
				if (!HasAura(itr->second->spellId))
					CastSpell(this, itr->second->spellId, true);
	}

	switch (pQuest->GetQuestId()) {
	case 26966:
	case 24528:
	case 26918:
	case 27023:
	case 10069: {
		if (this->HasSpell(20271))
			this->KilledMonsterCredit(44420, NULL);
	}
	default:
		break;
	}
	UpdateForQuestWorldObjects();
}

void Player::CompleteQuest(uint32 quest_id) {
	if (quest_id) {
		SetQuestStatus(quest_id, QUEST_STATUS_COMPLETE);

		uint16 log_slot = FindQuestSlot(quest_id);
		if (log_slot < MAX_QUEST_LOG_SIZE)
			SetQuestSlotState(log_slot, QUEST_STATE_COMPLETE);

		if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id)) {
			if (qInfo->HasFlag(QUEST_FLAGS_AUTO_REWARDED))
				RewardQuest(qInfo, 0, this, false);
			else
				SendQuestComplete(quest_id);
		}
	}
}

void Player::IncompleteQuest(uint32 quest_id) {
	if (quest_id) {
		SetQuestStatus(quest_id, QUEST_STATUS_INCOMPLETE);

		uint16 log_slot = FindQuestSlot(quest_id);
		if (log_slot < MAX_QUEST_LOG_SIZE)
			RemoveQuestSlotState(log_slot, QUEST_STATE_COMPLETE);
	}
}

void Player::RewardQuest(Quest const *pQuest, uint32 reward, Object* questGiver,
		bool announce) {
	//this THING should be here to protect code from quest, which cast on player far teleport as a reward
	//should work fine, cause far teleport will be executed in Player::Update()
	SetCanDelayTeleport(true);

	uint32 quest_id = pQuest->GetQuestId();

	for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
		if (pQuest->ReqItemId[i])
			DestroyItemCount(pQuest->ReqItemId[i], pQuest->ReqItemCount[i],
					true);

	for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) {
		if (pQuest->ReqSourceId[i]) {
			uint32 count = pQuest->ReqSourceCount[i];
			DestroyItemCount(pQuest->ReqSourceId[i], count ? count : 9999,
					true);
		}
	}

	RemoveTimedQuest(quest_id);

	if (pQuest->GetRewChoiceItemsCount() > 0) {
		if (uint32 itemId = pQuest->RewChoiceItemId[reward]) {
			ItemPosCountVec dest;
			if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId,
					pQuest->RewChoiceItemCount[reward]) == EQUIP_ERR_OK) {
				Item* item = StoreNewItem(dest, itemId, true,
						Item::GenerateItemRandomPropertyId(itemId));
				SendNewItem(item, pQuest->RewChoiceItemCount[reward], true,
						false);
			}
		}
	}

	if (pQuest->GetRewItemsCount() > 0) {
		for (uint32 i = 0; i < pQuest->GetRewItemsCount(); ++i) {
			if (uint32 itemId = pQuest->RewItemId[i]) {
				ItemPosCountVec dest;
				if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId,
						pQuest->RewItemCount[i]) == EQUIP_ERR_OK) {
					Item* item = StoreNewItem(dest, itemId, true,
							Item::GenerateItemRandomPropertyId(itemId));
					SendNewItem(item, pQuest->RewItemCount[i], true, false);
				}
			}
		}
	}

	RewardReputation(pQuest);

	uint16 log_slot = FindQuestSlot(quest_id);
	if (log_slot < MAX_QUEST_LOG_SIZE)
		SetQuestSlot(log_slot, 0);

	RewardedQuestSet::const_iterator rewItr = m_RewardedQuests.find(quest_id);
	bool rewarded = (rewItr != m_RewardedQuests.end());

	// Not give XP in case already completed once repeatable quest
	uint32 XP =
			rewarded ?
					0 :
					uint32(
							pQuest->XPValue(this)
									* sWorld->getRate(RATE_XP_QUEST));

	// handle SPELL_AURA_MOD_XP_QUEST_PCT auras
	Unit::AuraEffectList const& ModXPPctAuras = GetAuraEffectsByType(
			SPELL_AURA_MOD_XP_QUEST_PCT);
	for (Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin();
			i != ModXPPctAuras.end(); ++i)
		XP = uint32(XP * (1.0f + (*i)->GetAmount() / 100.0f));

	int32 moneyRew = 0;
	if (getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
		GiveXP(XP, NULL);
	else
		moneyRew = int32(
				pQuest->GetRewMoneyMaxLevel()
						* sWorld->getRate(RATE_DROP_MONEY));

	// If the player has a guild, it should gain 1/4 of his experience.
	// Despite of him being at max level or not.
	if (Guild * pGuild = sObjectMgr->GetGuildById(GetGuildId()))
		pGuild->GainXP(XP / 4);

	// Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative
	if (pQuest->GetRewOrReqMoney())
		moneyRew += pQuest->GetRewOrReqMoney();

	if (moneyRew) {
		ModifyMoney(moneyRew);

		if (moneyRew > 0)
			GetAchievementMgr().UpdateAchievementCriteria(
					ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD,
					uint32(moneyRew));
	}

	// honor reward
	if (pQuest->GetRewHonorAddition())
		RewardHonor(NULL, 0, pQuest->GetRewHonorAddition());
	if (pQuest->GetRewHonorMultiplier())
		RewardHonor(
				NULL,
				0,
				Trinity::Honor::hk_honor_at_level(getLevel(),
						pQuest->GetRewHonorMultiplier()));

	// title reward
	if (pQuest->GetCharTitleId()) {
		if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId()))
			SetTitle(titleEntry);
	}

	if (pQuest->GetBonusTalents()) {
		m_questRewardTalentCount += pQuest->GetBonusTalents();
		InitTalentForLevel();
	}

	// currencies reward
	for (uint32 i = 0; i < QUEST_CURRENCY_COUNT; i++) {
		uint32 currId = pQuest->GetRewCurrencyId(i);
		uint32 currCount = pQuest->GetRewCurrencyCount(i);
		if (currId && currCount)
			ModifyCurrency(currId, currCount * PLAYER_CURRENCY_PRECISION);
	}

	// Send reward mail
	if (uint32 mail_template_id = pQuest->GetRewMailTemplateId()) {
		//- TODO: Poor design of mail system
		SQLTransaction trans = CharacterDatabase.BeginTransaction();
		MailDraft(mail_template_id).SendMailTo(trans, this, questGiver,
				MAIL_CHECK_MASK_HAS_BODY, pQuest->GetRewMailDelaySecs());
		CharacterDatabase.CommitTransaction(trans);
	}

	if (pQuest->IsDaily() || pQuest->IsDFQuest()) {
		SetDailyQuestStatus(quest_id);
		if (pQuest->IsDaily()) {
			GetAchievementMgr().UpdateAchievementCriteria(
					ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST, quest_id);
			GetAchievementMgr().UpdateAchievementCriteria(
					ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY,
					quest_id);
		}
	} else if (pQuest->IsWeekly())
		SetWeeklyQuestStatus(quest_id);

	RemoveActiveQuest(quest_id);
	m_RewardedQuests.insert(quest_id);
	m_RewardedQuestsSave[quest_id] = true;

	if (announce)
		SendQuestReward(pQuest, XP, questGiver);

	// cast spells after mark quest complete (some spells have quest completed state reqyurements in spell_area data)
	if (pQuest->GetRewSpellCast() > 0)
		CastSpell(this, pQuest->GetRewSpellCast(), true);
	else if (pQuest->GetRewSpell() > 0)
		CastSpell(this, pQuest->GetRewSpell(), true);

	if (pQuest->GetZoneOrSort() > 0)
		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE,
				pQuest->GetZoneOrSort());
	GetAchievementMgr().UpdateAchievementCriteria(
			ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT);
	GetAchievementMgr().UpdateAchievementCriteria(
			ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST, pQuest->GetQuestId());

	uint32 zone = 0;
	uint32 area = 0;

	// remove auras from spells with quest reward state limitations
	SpellAreaForQuestMapBounds saEndBounds =
			sSpellMgr->GetSpellAreaForQuestEndMapBounds(quest_id);
	if (saEndBounds.first != saEndBounds.second) {
		GetZoneAndAreaId(zone, area);

		for (SpellAreaForAreaMap::const_iterator itr = saEndBounds.first;
				itr != saEndBounds.second; ++itr)
			if (!itr->second->IsFitToRequirements(this, zone, area))
				RemoveAurasDueToSpell(itr->second->spellId);
	}

	// Some spells applied at quest reward
	SpellAreaForQuestMapBounds saBounds =
			sSpellMgr->GetSpellAreaForQuestMapBounds(quest_id, false);
	if (saBounds.first != saBounds.second) {
		if (!zone || !area)
			GetZoneAndAreaId(zone, area);

		for (SpellAreaForAreaMap::const_iterator itr = saBounds.first;
				itr != saBounds.second; ++itr)
			if (itr->second->autocast
					&& itr->second->IsFitToRequirements(this, zone, area))
				if (!HasAura(itr->second->spellId))
					CastSpell(this, itr->second->spellId, true);
	}

	//lets remove flag for delayed teleports
	SetCanDelayTeleport(false);
}

void Player::FailQuest(uint32 questId) {
	if (Quest const* pQuest = sObjectMgr->GetQuestTemplate(questId)) {
		SetQuestStatus(questId, QUEST_STATUS_FAILED);

		uint16 log_slot = FindQuestSlot(questId);

		if (log_slot < MAX_QUEST_LOG_SIZE) {
			SetQuestSlotTimer(log_slot, 1);
			SetQuestSlotState(log_slot, QUEST_STATE_FAIL);
		}

		if (pQuest->HasFlag(QUEST_SPECIAL_FLAG_TIMED)) {
			QuestStatusData& q_status = m_QuestStatus[questId];

			RemoveTimedQuest(questId);
			q_status.m_timer = 0;

			SendQuestTimerFailed(questId);
		} else
			SendQuestFailed(questId);

		// Destroy quest items on quest failure.
		for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
			if (pQuest->ReqItemId[i] > 0 && pQuest->ReqItemCount[i] > 0)
				// Destroy items recieved on starting the quest.
				DestroyItemCount(pQuest->ReqItemId[i], pQuest->ReqItemCount[i],
						true, true);
		for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
			if (pQuest->ReqSourceId[i] > 0 && pQuest->ReqSourceCount[i] > 0)
				// Destroy items recieved during the quest.
				DestroyItemCount(pQuest->ReqSourceId[i],
						pQuest->ReqSourceCount[i], true, true);
	}
}

bool Player::SatisfyQuestSkillOrClass(Quest const* qInfo, bool msg) {
	int32 zoneOrSort = qInfo->GetZoneOrSort();
	int32 skillOrClassMask = qInfo->GetSkillOrClassMask();

	// skip zone zoneOrSort and 0 case skillOrClass
	if (zoneOrSort >= 0 && skillOrClassMask == 0)
		return true;

	int32 questSort = -zoneOrSort;
	uint8 reqSortClass = ClassByQuestSort(questSort);

	// check class sort cases in zoneOrSort
	if (reqSortClass != 0 && getClass() != reqSortClass) {
		if (msg)
			SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
		return false;
	}

	// check class
	if (skillOrClassMask < 0) {
		uint32 reqClassMask = -int32(skillOrClassMask);
		if (!(reqClassMask & getClassMask())) {
			if (msg)
				SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
			return false;
		}
	}
	// check skill
	else if (skillOrClassMask > 0) {
		uint32 reqSkill = skillOrClassMask;
		if (GetSkillValue(reqSkill) < qInfo->GetRequiredSkillValue()) {
			if (msg)
				SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
			return false;
		}
	}

	return true;
}

bool Player::SatisfyQuestLevel(Quest const* qInfo, bool msg) {
	if (getLevel() < qInfo->GetMinLevel()) {
		if (msg)
			SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_LOW_LEVEL);
		return false;
	} else if (qInfo->GetMaxLevel() > 0 && getLevel() > qInfo->GetMaxLevel()) {
		if (msg)
			SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); // There doesn't seem to be a specific response for too high player level
		return false;
	}
	return true;
}

bool Player::SatisfyQuestLog(bool msg) {
	// exist free slot
	if (FindQuestSlot(0) < MAX_QUEST_LOG_SIZE)
		return true;

	if (msg) {
		WorldPacket data(SMSG_QUESTLOG_FULL, 0);
		GetSession()->SendPacket(&data);
		sLog->outDebug(LOG_FILTER_NETWORKIO, "World: Sent SMSG_QUESTLOG_FULL");
	}
	return false;
}

bool Player::SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg) {
	// No previous quest (might be first quest in a series)
	if (qInfo->prevQuests.empty())
		return true;

	for (Quest::PrevQuests::const_iterator iter = qInfo->prevQuests.begin();
			iter != qInfo->prevQuests.end(); ++iter) {
		uint32 prevId = abs(*iter);

		Quest const* qPrevInfo = sObjectMgr->GetQuestTemplate(prevId);

		if (qPrevInfo) {
			// If any of the positive previous quests completed, return true
			if (*iter > 0
					&& m_RewardedQuests.find(prevId) != m_RewardedQuests.end()) {
				// skip one-from-all exclusive group
				if (qPrevInfo->GetExclusiveGroup() >= 0)
					return true;

				// each-from-all exclusive group (< 0)
				// can be start if only all quests in prev quest exclusive group completed and rewarded
				ObjectMgr::ExclusiveQuestGroups::iterator iter2 =
						sObjectMgr->mExclusiveQuestGroups.lower_bound(
								qPrevInfo->GetExclusiveGroup());
				ObjectMgr::ExclusiveQuestGroups::iterator end =
						sObjectMgr->mExclusiveQuestGroups.upper_bound(
								qPrevInfo->GetExclusiveGroup());

				ASSERT(iter2 != end); // always must be found if qPrevInfo->ExclusiveGroup != 0

				for (; iter2 != end; ++iter2) {
					uint32 exclude_Id = iter2->second;

					// skip checked quest id, only state of other quests in group is interesting
					if (exclude_Id == prevId)
						continue;

					// alternative quest from group also must be completed and rewarded(reported)
					if (m_RewardedQuests.find(exclude_Id)
							== m_RewardedQuests.end()) {
						if (msg)
							SendCanTakeQuestResponse(
									INVALIDREASON_DONT_HAVE_REQ);
						return false;
					}
				}
				return true;
			}

			// If any of the negative previous quests active, return true
			if (*iter < 0 && GetQuestStatus(prevId) != QUEST_STATUS_NONE) {
				// skip one-from-all exclusive group
				if (qPrevInfo->GetExclusiveGroup() >= 0)
					return true;

				// each-from-all exclusive group (< 0)
				// can be start if only all quests in prev quest exclusive group active
				ObjectMgr::ExclusiveQuestGroups::iterator iter2 =
						sObjectMgr->mExclusiveQuestGroups.lower_bound(
								qPrevInfo->GetExclusiveGroup());
				ObjectMgr::ExclusiveQuestGroups::iterator end =
						sObjectMgr->mExclusiveQuestGroups.upper_bound(
								qPrevInfo->GetExclusiveGroup());

				ASSERT(iter2 != end); // always must be found if qPrevInfo->ExclusiveGroup != 0

				for (; iter2 != end; ++iter2) {
					uint32 exclude_Id = iter2->second;

					// skip checked quest id, only state of other quests in group is interesting
					if (exclude_Id == prevId)
						continue;

					// alternative quest from group also must be active
					if (GetQuestStatus(exclude_Id) != QUEST_STATUS_NONE) {
						if (msg)
							SendCanTakeQuestResponse(
									INVALIDREASON_DONT_HAVE_REQ);
						return false;
					}
				}
				return true;
			}
		}
	}

	// Has only positive prev. quests in non-rewarded state
	// and negative prev. quests in non-active state
	if (msg)
		SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);

	return false;
}

bool Player::SatisfyQuestRace(Quest const* qInfo, bool msg) {
	uint32 reqraces = qInfo->GetRequiredRaces();
	if (reqraces == 0)
		return true;
	if ((reqraces & getRaceMask()) == 0) {
		if (msg)
			SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_WRONG_RACE);
		return false;
	}
	return true;
}

bool Player::SatisfyQuestReputation(Quest const* qInfo, bool msg) {
	uint32 fIdMin = qInfo->GetRequiredMinRepFaction(); //Min required rep
	if (fIdMin
			&& GetReputationMgr().GetReputation(fIdMin)
					< qInfo->GetRequiredMinRepValue()) {
		if (msg)
			SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
		return false;
	}

	uint32 fIdMax = qInfo->GetRequiredMaxRepFaction(); //Max required rep
	if (fIdMax
			&& GetReputationMgr().GetReputation(fIdMax)
					>= qInfo->GetRequiredMaxRepValue()) {
		if (msg)
			SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
		return false;
	}

	return true;
}

bool Player::SatisfyQuestStatus(Quest const* qInfo, bool msg) {
	if (GetQuestStatus(qInfo->GetQuestId()) != QUEST_STATUS_NONE) {
		if (msg)
			SendCanTakeQuestResponse(INVALIDREASON_QUEST_ALREADY_ON);
		return false;
	}
	return true;
}

bool Player::SatisfyQuestConditions(Quest const* qInfo, bool msg) {
	ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(
			CONDITION_SOURCE_TYPE_QUEST_ACCEPT, qInfo->GetQuestId());
	if (!sConditionMgr->IsPlayerMeetToConditions(this, conditions)) {
		if (msg)
			SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
		sLog->outDebug(
				LOG_FILTER_CONDITIONSYS,
				"Player::SatisfyQuestConditions: conditions not met for quest %u",
				qInfo->GetQuestId());
		return false;
	}
	return true;
}

bool Player::SatisfyQuestTimed(Quest const* qInfo, bool msg) {
	if (!m_timedquests.empty()
			&& qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_TIMED)) {
		if (msg)
			SendCanTakeQuestResponse(INVALIDREASON_QUEST_ONLY_ONE_TIMED);
		return false;
	}
	return true;
}

bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg) {
	// non positive exclusive group, if > 0 then can be start if any other quest in exclusive group already started/completed
	if (qInfo->GetExclusiveGroup() <= 0)
		return true;

	ObjectMgr::ExclusiveQuestGroups::iterator iter =
			sObjectMgr->mExclusiveQuestGroups.lower_bound(
					qInfo->GetExclusiveGroup());
	ObjectMgr::ExclusiveQuestGroups::iterator end =
			sObjectMgr->mExclusiveQuestGroups.upper_bound(
					qInfo->GetExclusiveGroup());

	ASSERT(iter != end); // always must be found if qInfo->ExclusiveGroup != 0

	for (; iter != end; ++iter) {
		uint32 exclude_Id = iter->second;

		// skip checked quest id, only state of other quests in group is interesting
		if (exclude_Id == qInfo->GetQuestId())
			continue;

		// not allow have daily quest if daily quest from exclusive group already recently completed
		Quest const* Nquest = sObjectMgr->GetQuestTemplate(exclude_Id);
		if (!SatisfyQuestDay(Nquest, false)
				|| !SatisfyQuestWeek(Nquest, false)) {
			if (msg)
				SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);

			return false;
		}

		// alternative quest already started or completed - but don't check rewarded states if both are repeatable
		if (GetQuestStatus(exclude_Id) != QUEST_STATUS_NONE
				|| (!(qInfo->IsRepeatable() && Nquest->IsRepeatable())
						&& m_RewardedQuests.find(exclude_Id)
								!= m_RewardedQuests.end())) {
			if (msg)
				SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
			return false;
		}
	}
	return true;
}

bool Player::SatisfyQuestNextChain(Quest const* qInfo, bool msg) {
	uint32 nextQuest = qInfo->GetNextQuestInChain();
	if (!nextQuest)
		return true;

	// next quest in chain already started or completed
	if (GetQuestStatus(nextQuest) != QUEST_STATUS_NONE) // GetQuestStatus returns QUEST_STATUS_COMPLETED for rewarded quests
			{
		if (msg)
			SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
		return false;
	}

	// check for all quests further up the chain
	// only necessary if there are quest chains with more than one quest that can be skipped
	//return SatisfyQuestNextChain(qInfo->GetNextQuestInChain(), msg);
	return true;
}

bool Player::SatisfyQuestPrevChain(Quest const* qInfo, bool msg) {
	// No previous quest in chain
	if (qInfo->prevChainQuests.empty())
		return true;

	for (Quest::PrevChainQuests::const_iterator iter =
			qInfo->prevChainQuests.begin();
			iter != qInfo->prevChainQuests.end(); ++iter) {
		QuestStatusMap::const_iterator itr = m_QuestStatus.find(*iter);

		// If any of the previous quests in chain active, return false
		if (itr != m_QuestStatus.end()
				&& itr->second.m_status != QUEST_STATUS_NONE) {
			if (msg)
				SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ);
			return false;
		}

		// check for all quests further down the chain
		// only necessary if there are quest chains with more than one quest that can be skipped
		//if (!SatisfyQuestPrevChain(prevId, msg))
		//    return false;
	}

	// No previous quest in chain active
	return true;
}

bool Player::SatisfyQuestDay(Quest const* qInfo, bool msg) {
	if (!qInfo->IsDaily() && !qInfo->IsDFQuest())
		return true;

	if (qInfo->IsDFQuest()) {
		if (!m_DFQuests.empty())
			return false;

		return true;
	}

	bool have_slot = false;
	for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS;
			++quest_daily_idx) {
		uint32 id = GetUInt32Value(
				PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx);
		if (qInfo->GetQuestId() == id)
			return false;

		if (!id)
			have_slot = true;
	}

	if (!have_slot) {
		if (msg)
			SendCanTakeQuestResponse(INVALIDREASON_DAILY_QUESTS_REMAINING);
		return false;
	}

	return true;
}

bool Player::SatisfyQuestWeek(Quest const* qInfo, bool /*msg*/) {
	if (!qInfo->IsWeekly() || m_weeklyquests.empty())
		return true;

	// if not found in cooldown list
	return m_weeklyquests.find(qInfo->GetQuestId()) == m_weeklyquests.end();
}

bool Player::GiveQuestSourceItem(Quest const *pQuest) {
	uint32 srcitem = pQuest->GetSrcItemId();
	if (srcitem > 0) {
		uint32 count = pQuest->GetSrcItemCount();
		if (count <= 0)
			count = 1;

		ItemPosCountVec dest;
		uint8 msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, srcitem, count);
		if (msg == EQUIP_ERR_OK) {
			Item * item = StoreNewItem(dest, srcitem, true);
			SendNewItem(item, count, true, false);
			return true;
		}
		// player already have max amount required item, just report success
		else if (msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS)
			return true;
		else
			SendEquipError(msg, NULL, NULL, srcitem);
		return false;
	}

	return true;
}

bool Player::TakeQuestSourceItem(uint32 quest_id, bool msg) {
	Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id);
	if (qInfo) {
		uint32 srcitem = qInfo->GetSrcItemId();
		if (srcitem > 0) {
			uint32 count = qInfo->GetSrcItemCount();
			if (count <= 0)
				count = 1;

			// exist one case when destroy source quest item not possible:
			// non un-equippable item (equipped non-empty bag, for example)
			uint8 res = CanUnequipItems(srcitem, count);
			if (res != EQUIP_ERR_OK) {
				if (msg)
					SendEquipError(res, NULL, NULL, srcitem);
				return false;
			}

			DestroyItemCount(srcitem, count, true, true);
		}
	}
	return true;
}

bool Player::GetQuestRewardStatus(uint32 quest_id) const {
	Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id);
	if (qInfo) {
		// for repeatable quests: rewarded field is set after first reward only to prevent getting XP more than once
		if (!qInfo->IsRepeatable())
			return m_RewardedQuests.find(quest_id) != m_RewardedQuests.end();

		return false;
	}
	return false;
}

QuestStatus Player::GetQuestStatus(uint32 quest_id) const {
	if (quest_id) {
		QuestStatusMap::const_iterator itr = m_QuestStatus.find(quest_id);
		if (itr != m_QuestStatus.end())
			return itr->second.m_status;

		if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id))
			if (!qInfo->IsRepeatable()
					&& m_RewardedQuests.find(quest_id) != m_RewardedQuests.end())
				return QUEST_STATUS_REWARDED;
	}
	return QUEST_STATUS_NONE;
}

bool Player::CanShareQuest(uint32 quest_id) const {
	Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id);
	if (qInfo && qInfo->HasFlag(QUEST_FLAGS_SHARABLE)) {
		QuestStatusMap::const_iterator itr = m_QuestStatus.find(quest_id);
		if (itr != m_QuestStatus.end())
			return itr->second.m_status == QUEST_STATUS_INCOMPLETE;
	}
	return false;
}

void Player::SetQuestStatus(uint32 quest_id, QuestStatus status) {
	if (sObjectMgr->GetQuestTemplate(quest_id)) {
		m_QuestStatus[quest_id].m_status = status;
		m_QuestStatusSave[quest_id] = true;
	}

	UpdateForQuestWorldObjects();
}

void Player::RemoveActiveQuest(uint32 quest_id) {
	QuestStatusMap::iterator itr = m_QuestStatus.find(quest_id);
	if (itr != m_QuestStatus.end()) {
		m_QuestStatus.erase(itr);
		m_QuestStatusSave[quest_id] = false;
		return;
	}
}

void Player::RemoveRewardedQuest(uint32 quest_id) {
	RewardedQuestSet::iterator rewItr = m_RewardedQuests.find(quest_id);
	if (rewItr != m_RewardedQuests.end()) {
		m_RewardedQuests.erase(rewItr);
		m_RewardedQuestsSave[quest_id] = false;
	}
}

// not used in Trinity, but used in scripting code
uint16 Player::GetReqKillOrCastCurrentCount(uint32 quest_id, int32 entry) {
	Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id);
	if (!qInfo)
		return 0;

	for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
		if (qInfo->ReqCreatureOrGOId[j] == entry)
			return m_QuestStatus[quest_id].m_creatureOrGOcount[j];

	return 0;
}

void Player::AdjustQuestReqItemCount(Quest const* pQuest,
		QuestStatusData& questStatusData) {
	if (pQuest->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER)) {
		for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) {
			uint32 reqitemcount = pQuest->ReqItemCount[i];
			if (reqitemcount != 0) {
				uint32 curitemcount = GetItemCount(pQuest->ReqItemId[i], true);

				questStatusData.m_itemcount[i] = std::min(curitemcount,
						reqitemcount);
				m_QuestStatusSave[pQuest->GetQuestId()] = true;
			}
		}
	}
}

uint16 Player::FindQuestSlot(uint32 quest_id) const {
	for (uint16 i = 0; i < MAX_QUEST_LOG_SIZE; ++i)
		if (GetQuestSlotQuestId(i) == quest_id)
			return i;

	return MAX_QUEST_LOG_SIZE;
}

void Player::AreaExploredOrEventHappens(uint32 questId) {
	if (questId) {
		uint16 log_slot = FindQuestSlot(questId);
		if (log_slot < MAX_QUEST_LOG_SIZE) {
			QuestStatusData& q_status = m_QuestStatus[questId];

			if (!q_status.m_explored) {
				q_status.m_explored = true;
				m_QuestStatusSave[questId] = true;
			}
		}
		if (CanCompleteQuest(questId))
			CompleteQuest(questId);
	}
}

//not used in Trinityd, function for external script library
void Player::GroupEventHappens(uint32 questId,
		WorldObject const* pEventObject) {
	if (Group *pGroup = GetGroup()) {
		for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr =
				itr->next()) {
			Player *pGroupGuy = itr->getSource();

			// for any leave or dead (with not released body) group member at appropriate distance
			if (pGroupGuy && pGroupGuy->IsAtGroupRewardDistance(pEventObject)
					&& !pGroupGuy->GetCorpse())
				pGroupGuy->AreaExploredOrEventHappens(questId);
		}
	} else
		AreaExploredOrEventHappens(questId);
}

void Player::ItemAddedQuestCheck(uint32 entry, uint32 count) {
	for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i) {
		uint32 questid = GetQuestSlotQuestId(i);
		if (questid == 0)
			continue;

		QuestStatusData& q_status = m_QuestStatus[questid];

		if (q_status.m_status != QUEST_STATUS_INCOMPLETE)
			continue;

		Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
		if (!qInfo || !qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER))
			continue;

		for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j) {
			uint32 reqitem = qInfo->ReqItemId[j];
			if (reqitem == entry) {
				uint32 reqitemcount = qInfo->ReqItemCount[j];
				uint16 curitemcount = q_status.m_itemcount[j];
				if (curitemcount < reqitemcount) {
					uint16 additemcount =
							curitemcount + count <= reqitemcount ?
									count : reqitemcount - curitemcount;
					q_status.m_itemcount[j] += additemcount;

					m_QuestStatusSave[questid] = true;

					SendQuestUpdateAddItem(qInfo, j, additemcount);
				}
				if (CanCompleteQuest(questid))
					CompleteQuest(questid);
				return;
			}
		}
	}
	UpdateForQuestWorldObjects();
}

void Player::ItemRemovedQuestCheck(uint32 entry, uint32 count) {
	for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i) {
		uint32 questid = GetQuestSlotQuestId(i);
		if (!questid)
			continue;
		Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
		if (!qInfo)
			continue;
		if (!qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER))
			continue;

		for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j) {
			uint32 reqitem = qInfo->ReqItemId[j];
			if (reqitem == entry) {
				QuestStatusData& q_status = m_QuestStatus[questid];

				uint32 reqitemcount = qInfo->ReqItemCount[j];
				uint16 curitemcount;
				if (q_status.m_status != QUEST_STATUS_COMPLETE)
					curitemcount = q_status.m_itemcount[j];
				else
					curitemcount = GetItemCount(entry, true);
				if (curitemcount < reqitemcount + count) {
					uint16 remitemcount =
							curitemcount <= reqitemcount ?
									count : count + reqitemcount - curitemcount;
					q_status.m_itemcount[j] =
							(curitemcount <= remitemcount) ?
									0 : curitemcount - remitemcount;

					m_QuestStatusSave[questid] = true;

					IncompleteQuest(questid);
				}
				return;
			}
		}
	}
	UpdateForQuestWorldObjects();
}

void Player::KilledMonster(CreatureInfo const* cInfo, uint64 guid) {
	if (cInfo->Entry)
		KilledMonsterCredit(cInfo->Entry, guid);

	for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i)
		if (cInfo->KillCredit[i])
			KilledMonsterCredit(cInfo->KillCredit[i], 0);
}

void Player::KilledMonsterCredit(uint32 entry, uint64 guid) {
	uint16 addkillcount = 1;
	uint32 real_entry = entry;
	if (guid) {
		Creature *killed = GetMap()->GetCreature(guid);
		if (killed && killed->GetEntry())
			real_entry = killed->GetEntry();
	}

	GetAchievementMgr().StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_CREATURE,
			real_entry); // MUST BE CALLED FIRST
	GetAchievementMgr().UpdateAchievementCriteria(
			ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, real_entry, addkillcount,
			guid ? GetMap()->GetCreature(guid) : NULL);

	for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i) {
		uint32 questid = GetQuestSlotQuestId(i);
		if (!questid)
			continue;

		Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
		if (!qInfo)
			continue;
		// just if !ingroup || !noraidgroup || raidgroup
		QuestStatusData& q_status = m_QuestStatus[questid];
		if (q_status.m_status == QUEST_STATUS_INCOMPLETE
				&& (!GetGroup() || !GetGroup()->isRaidGroup()
						|| qInfo->IsAllowedInRaid())) {
			if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_KILL_OR_CAST)) {
				for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j) {
					// skip GO activate objective or none
					if (qInfo->ReqCreatureOrGOId[j] <= 0)
						continue;

					// skip Cast at creature objective
					if (qInfo->ReqSpell[j] != 0)
						continue;

					uint32 reqkill = qInfo->ReqCreatureOrGOId[j];

					if (reqkill == real_entry) {
						uint32 reqkillcount = qInfo->ReqCreatureOrGOCount[j];
						uint16 curkillcount = q_status.m_creatureOrGOcount[j];
						if (curkillcount < reqkillcount) {
							q_status.m_creatureOrGOcount[j] = curkillcount
									+ addkillcount;

							m_QuestStatusSave[questid] = true;

							SendQuestUpdateAddCreatureOrGo(qInfo, guid, j,
									curkillcount, addkillcount);
						}
						if (CanCompleteQuest(questid))
							CompleteQuest(questid);

						// same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
						break;
					}
				}
			}
		}
	}
}

void Player::CastedCreatureOrGO(uint32 entry, uint64 guid, uint32 spell_id) {
	bool isCreature = IS_CRE_OR_VEH_GUID(guid);

	uint16 addCastCount = 1;
	for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i) {
		uint32 questid = GetQuestSlotQuestId(i);
		if (!questid)
			continue;

		Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
		if (!qInfo)
			continue;

		QuestStatusData& q_status = m_QuestStatus[questid];

		if (q_status.m_status == QUEST_STATUS_INCOMPLETE) {
			if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_KILL_OR_CAST)) {
				for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j) {
					// skip kill creature objective (0) or wrong spell casts
					if (qInfo->ReqSpell[j] != spell_id)
						continue;

					uint32 reqTarget = 0;

					if (isCreature) {
						// creature activate objectives
						if (qInfo->ReqCreatureOrGOId[j] > 0) {
							// checked at quest_template loading
							reqTarget = qInfo->ReqCreatureOrGOId[j];
							if (reqTarget != entry) // if entry doesn't match, check for killcredits referenced in template
									{
								CreatureInfo const *cinfo =
										ObjectMgr::GetCreatureTemplate(entry);
								for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i)
									if (cinfo->KillCredit[i] == reqTarget)
										entry = cinfo->KillCredit[i];
							}
						}
					} else {
						// GO activate objective
						if (qInfo->ReqCreatureOrGOId[j] < 0)
							// checked at quest_template loading
							reqTarget = -qInfo->ReqCreatureOrGOId[j];
					}

					// other not this creature/GO related objectives
					if (reqTarget != entry)
						continue;

					uint32 reqCastCount = qInfo->ReqCreatureOrGOCount[j];
					uint16 curCastCount = q_status.m_creatureOrGOcount[j];
					if (curCastCount < reqCastCount) {
						q_status.m_creatureOrGOcount[j] = curCastCount
								+ addCastCount;

						m_QuestStatusSave[questid] = true;

						SendQuestUpdateAddCreatureOrGo(qInfo, guid, j,
								curCastCount, addCastCount);
					}

					if (CanCompleteQuest(questid))
						CompleteQuest(questid);

					// same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
					break;
				}
			}
		}
	}
}

void Player::TalkedToCreature(uint32 entry, uint64 guid) {
	uint16 addTalkCount = 1;
	for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i) {
		uint32 questid = GetQuestSlotQuestId(i);
		if (!questid)
			continue;

		Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
		if (!qInfo)
			continue;

		QuestStatusData& q_status = m_QuestStatus[questid];

		if (q_status.m_status == QUEST_STATUS_INCOMPLETE) {
			if (qInfo->HasSpecialFlag(
					QUEST_SPECIAL_FLAG_KILL_OR_CAST
							| QUEST_SPECIAL_FLAG_SPEAKTO)) {
				for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j) {
					// skip spell casts and Gameobject objectives
					if (qInfo->ReqSpell[j] > 0
							|| qInfo->ReqCreatureOrGOId[j] < 0)
						continue;

					uint32 reqTarget = 0;

					if (qInfo->ReqCreatureOrGOId[j] > 0) // creature activate objectives
														 // checked at quest_template loading
						reqTarget = qInfo->ReqCreatureOrGOId[j];
					else
						continue;

					if (reqTarget == entry) {
						uint32 reqTalkCount = qInfo->ReqCreatureOrGOCount[j];
						uint16 curTalkCount = q_status.m_creatureOrGOcount[j];
						if (curTalkCount < reqTalkCount) {
							q_status.m_creatureOrGOcount[j] = curTalkCount
									+ addTalkCount;

							m_QuestStatusSave[questid] = true;

							SendQuestUpdateAddCreatureOrGo(qInfo, guid, j,
									curTalkCount, addTalkCount);
						}
						if (CanCompleteQuest(questid))
							CompleteQuest(questid);

						// same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
						continue;
					}
				}
			}
		}
	}
}

void Player::MoneyChanged(uint32 count) {
	for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i) {
		uint32 questid = GetQuestSlotQuestId(i);
		if (!questid)
			continue;

		Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
		if (qInfo && qInfo->GetRewOrReqMoney() < 0) {
			QuestStatusData& q_status = m_QuestStatus[questid];

			if (q_status.m_status == QUEST_STATUS_INCOMPLETE) {
				if (int32(count) >= -qInfo->GetRewOrReqMoney()) {
					if (CanCompleteQuest(questid))
						CompleteQuest(questid);
				}
			} else if (q_status.m_status == QUEST_STATUS_COMPLETE) {
				if (int32(count) < -qInfo->GetRewOrReqMoney())
					IncompleteQuest(questid);
			}
		}
	}
}

void Player::ReputationChanged(FactionEntry const* factionEntry) {
	for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i) {
		if (uint32 questid = GetQuestSlotQuestId(i)) {
			if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid)) {
				if (qInfo->GetRepObjectiveFaction() == factionEntry->ID) {
					QuestStatusData& q_status = m_QuestStatus[questid];
					if (q_status.m_status == QUEST_STATUS_INCOMPLETE) {
						if (GetReputationMgr().GetReputation(factionEntry)
								>= qInfo->GetRepObjectiveValue())
							if (CanCompleteQuest(questid))
								CompleteQuest(questid);
					} else if (q_status.m_status == QUEST_STATUS_COMPLETE) {
						if (GetReputationMgr().GetReputation(factionEntry)
								< qInfo->GetRepObjectiveValue())
							IncompleteQuest(questid);
					}
				}
			}
		}
	}
}

void Player::ReputationChanged2(FactionEntry const* factionEntry) {
	for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i) {
		if (uint32 questid = GetQuestSlotQuestId(i)) {
			if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid)) {
				if (qInfo->GetRepObjectiveFaction2() == factionEntry->ID) {
					QuestStatusData& q_status = m_QuestStatus[questid];
					if (q_status.m_status == QUEST_STATUS_INCOMPLETE) {
						if (GetReputationMgr().GetReputation(factionEntry)
								>= qInfo->GetRepObjectiveValue2())
							if (CanCompleteQuest(questid))
								CompleteQuest(questid);
					} else if (q_status.m_status == QUEST_STATUS_COMPLETE) {
						if (GetReputationMgr().GetReputation(factionEntry)
								< qInfo->GetRepObjectiveValue2())
							IncompleteQuest(questid);
					}
				}
			}
		}
	}
}

bool Player::HasQuestForItem(uint32 itemid) const {
	for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i) {
		uint32 questid = GetQuestSlotQuestId(i);
		if (questid == 0)
			continue;

		QuestStatusMap::const_iterator qs_itr = m_QuestStatus.find(questid);
		if (qs_itr == m_QuestStatus.end())
			continue;

		QuestStatusData const& q_status = qs_itr->second;

		if (q_status.m_status == QUEST_STATUS_INCOMPLETE) {
			Quest const* qinfo = sObjectMgr->GetQuestTemplate(questid);
			if (!qinfo)
				continue;

			// hide quest if player is in raid-group and quest is no raid quest
			if (GetGroup() && GetGroup()->isRaidGroup()
					&& !qinfo->IsAllowedInRaid())
				if (!InBattleground()) //there are two ways.. we can make every bg-quest a raidquest, or add this code here.. i don't know if this can be exploited by other quests, but i think all other quests depend on a specific area.. but keep this in mind, if something strange happens later
					continue;

			// There should be no mixed ReqItem/ReqSource drop
			// This part for ReqItem drop
			for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j) {
				if (itemid == qinfo->ReqItemId[j]
						&& q_status.m_itemcount[j] < qinfo->ReqItemCount[j])
					return true;
			}
			// This part - for ReqSource
			for (uint8 j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j) {
				// examined item is a source item
				if (qinfo->ReqSourceId[j] == itemid) {
					ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(
							itemid);

					// 'unique' item
					if (pProto->MaxCount
							&& int32(GetItemCount(itemid, true))
									< pProto->MaxCount)
						return true;

					// allows custom amount drop when not 0
					if (qinfo->ReqSourceCount[j]) {
						if (GetItemCount(itemid, true)
								< qinfo->ReqSourceCount[j])
							return true;
					} else if (GetItemCount(itemid, true)
							< pProto->GetMaxStackSize())
						return true;
				}
			}
		}
	}
	return false;
}

void Player::SendQuestComplete(uint32 quest_id) {
	if (quest_id) {
		WorldPacket data(SMSG_QUESTUPDATE_COMPLETE, 4);
		data << uint32(quest_id);
		GetSession()->SendPacket(&data);
		sLog->outDebug(LOG_FILTER_NETWORKIO,
				"World: Sent SMSG_QUESTUPDATE_COMPLETE quest = %u", quest_id);
	}
}

void Player::SendQuestReward(Quest const *pQuest, uint32 XP,
		Object * questGiver) {
	uint32 questid = pQuest->GetQuestId();
	sLog->outDebug(LOG_FILTER_NETWORKIO,
			"World: Sent SMSG_QUESTGIVER_QUEST_COMPLETE quest = %u", questid);
	sGameEventMgr->HandleQuestComplete(questid);
	WorldPacket data(SMSG_QUESTGIVER_QUEST_COMPLETE, (4 + 4 + 4 + 4 + 4));
	data << uint8(0x80); // unk 4.0.1 flags
	data << uint32(pQuest->GetRewSkillLineId());
	data << uint32(questid);

	if (getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) {
		data << uint32(pQuest->GetRewOrReqMoney());
		data << uint32(pQuest->GetBonusTalents()); // bonus talents
		data << uint32(pQuest->GetRewSkillPoints());
		data << uint32(XP);
	} else {
		data
				<< uint32(
						pQuest->GetRewOrReqMoney()
								+ int32(
										pQuest->GetRewMoneyMaxLevel()
												* sWorld->getRate(
														RATE_DROP_MONEY)));
		data << uint32(pQuest->GetRewSkillPoints()); // bonus talents
		data << uint32(0);

		data << uint32(0);
	}

	GetSession()->SendPacket(&data);

	if (pQuest->GetQuestCompleteScript() != 0)
		GetMap()->ScriptsStart(sQuestEndScripts,
				pQuest->GetQuestCompleteScript(), questGiver, this);
}

void Player::SendQuestFailed(uint32 quest_id) {
	if (quest_id) {
		WorldPacket data(SMSG_QUESTGIVER_QUEST_FAILED, 4 + 4);
		data << uint32(quest_id);
		data << uint32(0); // failed reason (4 for inventory is full)
		GetSession()->SendPacket(&data);
		sLog->outDebug(LOG_FILTER_NETWORKIO,
				"World: Sent SMSG_QUESTGIVER_QUEST_FAILED");
	}
}

void Player::SendQuestWindowClose(uint32 questId)
{
    if (questId)
    {
        WorldPacket data(SMSG_QUESTGIVER_QUEST_FAILED, 4 + 4);
        GetSession()->SendPacket(&data);
        sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_QUESTGIVER_QUEST_FAILED for WindowClose");
    }
}

void Player::SendQuestTimerFailed(uint32 quest_id) {
	if (quest_id) {
		WorldPacket data(SMSG_QUESTUPDATE_FAILEDTIMER, 4);
		data << uint32(quest_id);
		GetSession()->SendPacket(&data);
		sLog->outDebug(LOG_FILTER_NETWORKIO,
				"World: Sent SMSG_QUESTUPDATE_FAILEDTIMER");
	}
}

void Player::SendCanTakeQuestResponse(uint32 msg) {
	WorldPacket data(SMSG_QUESTGIVER_QUEST_INVALID, 4);
	data << uint32(msg);
	GetSession()->SendPacket(&data);
	sLog->outDebug(LOG_FILTER_NETWORKIO,
			"World: Sent SMSG_QUESTGIVER_QUEST_INVALID");
}

void Player::SendQuestConfirmAccept(const Quest* pQuest, Player* pReceiver) {
	if (pReceiver) {
		std::string strTitle = pQuest->GetTitle();

		int loc_idx = pReceiver->GetSession()->GetSessionDbLocaleIndex();
		if (loc_idx >= 0)
			if (const QuestLocale* pLocale = sObjectMgr->GetQuestLocale(pQuest->GetQuestId()))
				sObjectMgr->GetLocaleString(pLocale->Title, loc_idx, strTitle);

		WorldPacket data(SMSG_QUEST_CONFIRM_ACCEPT, (4 + strTitle.size() + 8));
		data << uint32(pQuest->GetQuestId());
		data << strTitle;
		data << uint64(GetGUID());
		pReceiver->GetSession()->SendPacket(&data);

		sLog->outDebug(LOG_FILTER_NETWORKIO,
				"World: Sent SMSG_QUEST_CONFIRM_ACCEPT");
	}
}

void Player::SendPushToPartyResponse(Player *pPlayer, uint32 msg) {
	if (pPlayer) {
		WorldPacket data(MSG_QUEST_PUSH_RESULT, (8 + 1));
		data << uint64(pPlayer->GetGUID());
		data << uint8(msg); // valid values: 0-8
		GetSession()->SendPacket(&data);
		sLog->outDebug(LOG_FILTER_NETWORKIO,
				"World: Sent MSG_QUEST_PUSH_RESULT");
	}
}

void Player::SendQuestUpdateAddItem(Quest const* /*pQuest*/,
		uint32 /*item_idx*/, uint16 /*count*/) {
	WorldPacket data(SMSG_QUESTUPDATE_ADD_ITEM, 0);
	sLog->outDebug(LOG_FILTER_NETWORKIO,
			"World: Sent SMSG_QUESTUPDATE_ADD_ITEM");
	//data << pQuest->ReqItemId[item_idx];
	//data << count;
	GetSession()->SendPacket(&data);
}

void Player::SendQuestUpdateAddCreatureOrGo(Quest const* pQuest, uint64 guid,
		uint32 creatureOrGO_idx, uint16 old_count, uint16 add_count) {
	ASSERT(old_count + add_count < 65536 && "mob/GO count store in 16 bits 2^16 = 65536 (0..65536)");

	int32 entry = pQuest->ReqCreatureOrGOId[creatureOrGO_idx];
	if (entry < 0)
		// client expected gameobject template id in form (id|0x80000000)
		entry = (-entry) | 0x80000000;

	WorldPacket data(SMSG_QUESTUPDATE_ADD_KILL, (4 * 4 + 8));
	sLog->outDebug(LOG_FILTER_NETWORKIO,
			"World: Sent SMSG_QUESTUPDATE_ADD_KILL");
	data << uint32(pQuest->GetQuestId());
	data << uint32(entry);
	data << uint32(old_count + add_count);
	data << uint32(pQuest->ReqCreatureOrGOCount[creatureOrGO_idx]);
	data << uint64(guid);
	GetSession()->SendPacket(&data);

	uint16 log_slot = FindQuestSlot(pQuest->GetQuestId());
	if (log_slot < MAX_QUEST_LOG_SIZE)
		SetQuestSlotCounter(log_slot, creatureOrGO_idx,
				GetQuestSlotCounter(log_slot, creatureOrGO_idx) + add_count);
}

/*********************************************************/
/***                   LOAD SYSTEM                     ***/
/*********************************************************/

void Player::Initialize(uint32 guid) {
	Object::_Create(guid, 0, HIGHGUID_PLAYER);
}

void Player::_LoadDeclinedNames(PreparedQueryResult result) {
	if (!result)
		return;

	delete m_declinedname;
	m_declinedname = new DeclinedName;
	for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
		m_declinedname->name[i] = (*result)[i].GetString();
}

void Player::_LoadArenaTeamInfo(PreparedQueryResult result) {
	// arenateamid, played_week, played_season, personal_rating
	memset((void*) &m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1], 0,
			sizeof(uint32) * MAX_ARENA_SLOT * ARENA_TEAM_END);

	if (!result)
		return;

	do {
		Field* fields = result->Fetch();

		uint32 arenaTeamId = fields[0].GetUInt32();

		ArenaTeam* arenaTeam = sObjectMgr->GetArenaTeamById(arenaTeamId);
		if (!arenaTeam) {
			sLog->outError(
					"Player::_LoadArenaTeamInfo: couldn't load arenateam %u",
					arenaTeamId);
			continue;
		}

		uint8 arenaSlot = arenaTeam->GetSlot();

		SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_ID, arenaTeamId);
		SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_TYPE, arenaTeam->GetType());
		SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_MEMBER,
				(arenaTeam->GetCaptain() == GetGUID()) ? 0 : 1);
		SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_GAMES_WEEK,
				uint32(fields[1].GetUInt16()));
		SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_GAMES_SEASON,
				uint32(fields[2].GetUInt16()));
		SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_WINS_SEASON,
				uint32(fields[3].GetUInt16()));
	} while (result->NextRow());
}

void Player::_LoadArenaStatsInfo(PreparedQueryResult result) {
	uint16 personalRatingCache[] = { 0, 0, 0 };

	if (result) {
		do {
			Field* fields = result->Fetch();

			personalRatingCache[fields[0].GetUInt8()] = fields[1].GetUInt16();
		} while (result->NextRow());
	}

	for (uint8 slot = 0; slot <= 2; ++slot) {
		SetArenaTeamInfoField(slot, ARENA_TEAM_PERSONAL_RATING,
				uint32(personalRatingCache[slot]));
	}
}

void Player::_LoadEquipmentSets(PreparedQueryResult result) {
	// SetPQuery(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS,   "SELECT setguid, setindex, name, iconname, item0, item1, item2, item3, item4, item5, item6, item7, item8, item9, item10, item11, item12, item13, item14, item15, item16, item17, item18 FROM character_equipmentsets WHERE guid = '%u' ORDER BY setindex", GUID_LOPART(m_guid));
	if (!result)
		return;

	uint32 count = 0;
	do {
		Field* fields = result->Fetch();
		EquipmentSet eqSet;

		eqSet.Guid = fields[0].GetUInt64();
		uint32 index = fields[1].GetUInt8();
		eqSet.Name = fields[2].GetString();
		eqSet.IconName = fields[3].GetString();
		eqSet.state = EQUIPMENT_SET_UNCHANGED;

		for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
			eqSet.Items[i] = fields[4 + i].GetUInt32();

		m_EquipmentSets[index] = eqSet;

		++count;

		if (count >= MAX_EQUIPMENT_SET_INDEX) // client limit
			break;
	} while (result->NextRow());
}

void Player::_LoadBGData(PreparedQueryResult result) {
	if (!result)
		return;

	Field* fields = result->Fetch();
	// Expecting only one row
	//        0           1     2      3      4      5      6          7          8        9
	// SELECT instanceId, team, joinX, joinY, joinZ, joinO, joinMapId, taxiStart, taxiEnd, mountSpell FROM character_battleground_data WHERE guid = ?

	m_bgData.bgInstanceID = fields[0].GetUInt32();
	m_bgData.bgTeam = fields[1].GetUInt16();
	m_bgData.joinPos = WorldLocation(fields[6].GetUInt16(), // Map
			fields[2].GetFloat(), // X
			fields[3].GetFloat(), // Y
			fields[4].GetFloat(), // Z
			fields[5].GetFloat()); // Orientation
	m_bgData.taxiPath[0] = fields[7].GetUInt32();
	m_bgData.taxiPath[1] = fields[8].GetUInt32();
	m_bgData.mountSpell = fields[9].GetUInt32();
}

bool Player::LoadPositionFromDB(uint32& mapid, float& x, float& y, float& z,
		float& o, bool& in_flight, uint64 guid) {
	QueryResult result =
			CharacterDatabase.PQuery(
					"SELECT position_x, position_y, position_z, orientation, map, taxi_path FROM characters WHERE guid = '%u'",
					GUID_LOPART(guid));
	if (!result)
		return false;

	Field *fields = result->Fetch();

	x = fields[0].GetFloat();
	y = fields[1].GetFloat();
	z = fields[2].GetFloat();
	o = fields[3].GetFloat();
	mapid = fields[4].GetUInt16();
	in_flight = !fields[5].GetString().empty();

	return true;
}

void Player::SetHomebind(WorldLocation const& /*loc*/, uint32 /*area_id*/) {
	m_homebindMapId = GetMapId();
	m_homebindAreaId = GetAreaId();
	m_homebindX = GetPositionX();
	m_homebindY = GetPositionY();
	m_homebindZ = GetPositionZ();

	// update sql homebind
	CharacterDatabase.PExecute(
			"UPDATE character_homebind SET map = '%u', zone = '%u', position_x = '%f', position_y = '%f', position_z = '%f' WHERE guid = '%u'",
			m_homebindMapId, m_homebindAreaId, m_homebindX, m_homebindY,
			m_homebindZ, GetGUIDLow());
}

uint32 Player::GetUInt32ValueFromArray(Tokens const& data, uint16 index) {
	if (index >= data.size())
		return 0;

	return (uint32) atoi(data[index]);
}

float Player::GetFloatValueFromArray(Tokens const& data, uint16 index) {
	float result;
	uint32 temp = Player::GetUInt32ValueFromArray(data, index);
	memcpy(&result, &temp, sizeof(result));

	return result;
}

Player* Player::LoadFromDB(uint32 guid, SQLQueryHolder * holder,
		WorldSession * session) {
	////                                                     0     1        2     3     4        5      6    7      8     9           10              11
	//QueryResult *result = CharacterDatabase.PQuery("SELECT guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, "
	// 12          13          14          15   16           17        18        19         20         21          22           23                 24
	//"position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, "
	// 25                 26       27       28       29       30         31           32             33        34    35      36                 37         38
	//"resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask, "
	// 39           40                41                 42                    43          44          45              46           47               48              49
	//"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, "
	// 50      51      52      53      54      55      56      57      58      59      60       61           62         63          64             65              66
	//"health, power1, power2, power3, power4, power5, power6, power7, power8, power9, power10, instance_id, speccount, activespec, exploredZones, equipmentCache, ammoId, "
	// 67           68          69              70
	//"knownTitles, actionBars, currentPetSlot, petSlotUsed FROM characters WHERE guid = '%u'", guid);
	PreparedQueryResult result = holder->GetPreparedResult(
			PLAYER_LOGIN_QUERY_LOADFROM);

	if (!result) {
		sLog->outError(
				"Player (GUID: %u) not found in table `characters`, can't load. ",
				guid);
		return false;
	}

	Field* fields = result->Fetch();

	uint8 pClass = fields[4].GetUInt8();

	Player* player = NULL;
	switch (pClass) {
	case CLASS_WARRIOR:
		player = new WarriorPlayer(session);
		break;
	case CLASS_PALADIN:
		player = new PaladinPlayer(session);
		break;
	case CLASS_HUNTER:
		player = new HunterPlayer(session);
		break;
	case CLASS_ROGUE:
		player = new RoguePlayer(session);
		break;
	case CLASS_PRIEST:
		player = new PriestPlayer(session);
		break;
	case CLASS_DEATH_KNIGHT:
		player = new DKPlayer(session);
		break;
	case CLASS_SHAMAN:
		player = new ShamanPlayer(session);
		break;
	case CLASS_MAGE:
		player = new MagePlayer(session);
		break;
	case CLASS_WARLOCK:
		player = new WarlockPlayer(session);
		break;
	case CLASS_DRUID:
		player = new DruidPlayer(session);
		break;
	default:
		printf("\nClass %u doesn't exist.\n", pClass);
		break;
	}

	if (player && player)
		if (player->_LoadFromDB(guid, holder, result))
			return player;

	return NULL;
}

// We send the result to not waste time to do it again
bool Player::_LoadFromDB(uint32 guid, SQLQueryHolder * holder,
		PreparedQueryResult & result) {
	//TODO : drop ammoid & stable_slots

	////                                                     0     1        2     3     4        5      6    7      8     9           10              11
	//QueryResult *result = CharacterDatabase.PQuery("SELECT guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, "
	// 12          13          14          15   16           17        18        19         20         21          22           23                 24
	//"position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, "
	// 25                 26       27       28       29       30         31           32             33        34    35      36                 37         38
	//"resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask, "
	// 39           40                41                 42                    43          44          45              46           47               48              49
	//"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, "
	// 50      51      52      53      54      55      56      57      58      59      60       61           62         63          64             65              66
	//"health, power1, power2, power3, power4, power5, power6, power7, power8, power9, power10, instance_id, speccount, activespec, exploredZones, equipmentCache, ammoId, "
	// 67           68          69              70
	//"knownTitles, actionBars, currentPetSlot, petSlotUsed FROM characters WHERE guid = '%u'", guid);

	Field* fields = result->Fetch();

	//uint32 dbAccountId = fields[1].GetUInt32();

	// check if the character's account in the db and the logged in account match.
	// player should be able to load/delete character only with correct account!
	/* TODO: restore check */

	if (holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADBANNED)) {
		sLog->outError("Player (GUID: %u) is banned, can't load.", guid);
		return false;
	}

	Object::_Create(guid, 0, HIGHGUID_PLAYER);

	m_name = fields[2].GetString();

	// check name limitations
	if (ObjectMgr::CheckPlayerName(m_name) != CHAR_NAME_SUCCESS
			|| (GetSession()->GetSecurity() == SEC_PLAYER
					&& sObjectMgr->IsReservedName(m_name))) {
		CharacterDatabase.PExecute(
				"UPDATE characters SET at_login = at_login | '%u' WHERE guid ='%u'",
				uint32(AT_LOGIN_RENAME), guid);
		return false;
	}

	// overwrite possible wrong/corrupted guid
	SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));

	// overwrite some data fields
	uint32 bytes0 = 0;
	bytes0 |= fields[3].GetUInt8(); // race
	bytes0 |= fields[4].GetUInt8() << 8; // class
	bytes0 |= fields[5].GetUInt8() << 16; // gender
	SetUInt32Value(UNIT_FIELD_BYTES_0, bytes0);

	SetUInt32Value(UNIT_FIELD_LEVEL, fields[6].GetUInt8());
	SetUInt32Value(PLAYER_XP, fields[7].GetUInt32());

	_LoadIntoDataField(fields[64].GetCString(), PLAYER_EXPLORED_ZONES_1,
			PLAYER_EXPLORED_ZONES_SIZE);
	_LoadIntoDataField(fields[67].GetCString(), PLAYER__FIELD_KNOWN_TITLES,
			KNOWN_TITLES_SIZE * 2);

	SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE);
	SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f);
	SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f);

	SetUInt32Value(PLAYER_GUILDRANK, 0);
	SetUInt32Value(PLAYER_GUILD_TIMESTAMP, 0);
	SetUInt32Value(PLAYER_GUILDDELETE_DATE, 0);
	SetUInt32Value(PLAYER_GUILDLEVEL, 1);

	uint32 money = fields[8].GetUInt32();
	if (money > MAX_MONEY_AMOUNT)
		money = MAX_MONEY_AMOUNT;
	SetMoney(money);

	SetUInt32Value(PLAYER_BYTES, fields[9].GetUInt32());
	SetUInt32Value(PLAYER_BYTES_2, fields[10].GetUInt32());
	SetUInt32Value(PLAYER_BYTES_3,
			(fields[49].GetUInt16() & 0xFFFE) | fields[5].GetUInt8());
	SetUInt32Value(PLAYER_FLAGS, fields[11].GetUInt32());
	SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fields[48].GetUInt32());

	//SetUInt64Value(PLAYER_FIELD_KNOWN_CURRENCIES, fields[47].GetUInt64());

	//SetUInt32Value(PLAYER_AMMO_ID, fields[63].GetUInt32());

	// set which actionbars the client has active - DO NOT REMOVE EVER AGAIN (can be changed though, if it does change fieldwise)
	SetByteValue(PLAYER_FIELD_BYTES, 2, fields[68].GetUInt8());

	m_currentPetSlot = (PetSlot) fields[69].GetUInt32();
	m_petSlotUsed = fields[70].GetUInt32();

	InitDisplayIds();

	// cleanup inventory related item value fields (its will be filled correctly in _LoadInventory)
	for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) {
		SetUInt64Value(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), 0);
		SetVisibleItemSlot(slot, NULL);

		delete m_items[slot];
		m_items[slot] = NULL;
	}

	sLog->outDebug(LOG_FILTER_PLAYER_LOADING,
			"Load Basic value of player %s is: ", m_name.c_str());
	outDebugValues();

	//Need to call it to initialize m_team (m_team can be calculated from race)
	//Other way is to saves m_team into characters table.
	setFactionForRace(getRace());

	// load home bind and check in same time class/race pair, it used later for restore broken positions
	if (!_LoadHomeBind(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND)))
		return false;

	InitPrimaryProfessions(); // to max set before any spell loaded

	// init saved position, and fix it later if problematic
	uint32 transGUID = uint32(fields[30].GetUInt64()); // field type is uint64 but lowguid is saved
	Relocate(fields[12].GetFloat(), fields[13].GetFloat(),
			fields[14].GetFloat(), fields[16].GetFloat());
	uint32 mapId = fields[15].GetUInt32();
	uint32 instanceId = fields[61].GetUInt32();

	uint32 dungeonDiff = fields[38].GetUInt8() & 0x0F;
	if (dungeonDiff >= MAX_DUNGEON_DIFFICULTY)
		dungeonDiff = DUNGEON_DIFFICULTY_NORMAL;
	uint32 raidDiff = (fields[38].GetUInt8() >> 4) & 0x0F;
	if (raidDiff >= MAX_RAID_DIFFICULTY)
		raidDiff = RAID_DIFFICULTY_10MAN_NORMAL;
	SetDungeonDifficulty(Difficulty(dungeonDiff)); // may be changed in _LoadGroup
	SetRaidDifficulty(Difficulty(raidDiff)); // may be changed in _LoadGroup

	std::string taxi_nodes = fields[37].GetString();

#define RelocateToHomebind(){ mapId = m_homebindMapId; instanceId = 0; Relocate(m_homebindX, m_homebindY, m_homebindZ); }

	_LoadGroup(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADGROUP));

	_LoadArenaTeamInfo(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADARENAINFO));
	_LoadArenaStatsInfo(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADARENASTATS));

	uint32 arena_currency = fields[39].GetUInt32();
	if (arena_currency > sWorld->getIntConfig(CONFIG_MAX_JUSTICE_POINTS))
		arena_currency = sWorld->getIntConfig(CONFIG_MAX_JUSTICE_POINTS);

	// check arena teams integrity
	for (uint32 arena_slot = 0; arena_slot < MAX_ARENA_SLOT; ++arena_slot) {
		uint32 arena_team_id = GetArenaTeamId(arena_slot);
		if (!arena_team_id)
			continue;

		if (ArenaTeam * at = sObjectMgr->GetArenaTeamById(arena_team_id))
			if (at->IsMember(GetGUID()))
				continue;

		// arena team not exist or not member, cleanup fields
		for (int j = 0; j < 6; ++j)
			SetArenaTeamInfoField(arena_slot, ArenaTeamInfoType(j), 0);
	}

	SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS,
			fields[45].GetUInt32());
	SetUInt16Value(PLAYER_FIELD_KILLS, 0, fields[46].GetUInt16());
	SetUInt16Value(PLAYER_FIELD_KILLS, 1, fields[47].GetUInt16());

	_LoadBoundInstances(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES));
	_LoadInstanceTimeRestrictions(
			holder->GetPreparedResult(
					PLAYER_LOGIN_QUERY_LOADINSTANCELOCKTIMES));
	_LoadBGData(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADBGDATA));

	MapEntry const * mapEntry = sMapStore.LookupEntry(mapId);
	if (!mapEntry || !IsPositionValid()) {
		sLog->outError(
				"Player (guidlow %d) have invalid coordinates (MapId: %u X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",
				guid, mapId, GetPositionX(), GetPositionY(), GetPositionZ(),
				GetOrientation());
		RelocateToHomebind();
	}
	// Player was saved in Arena or Bg
	else if (mapEntry && mapEntry->IsBattlegroundOrArena()) {
		Battleground *currentBg = NULL;
		if (m_bgData.bgInstanceID) //saved in Battleground
			currentBg = sBattlegroundMgr->GetBattleground(m_bgData.bgInstanceID,
					BATTLEGROUND_TYPE_NONE);

		bool player_at_bg = currentBg
				&& currentBg->IsPlayerInBattleground(GetGUID());

		if (player_at_bg && currentBg->GetStatus() != STATUS_WAIT_LEAVE) {
			BattlegroundQueueTypeId bgQueueTypeId =
					sBattlegroundMgr->BGQueueTypeId(currentBg->GetTypeID(true),
							currentBg->GetArenaType());
			AddBattlegroundQueueId(bgQueueTypeId);

			m_bgData.bgTypeID = currentBg->GetTypeID(true);

			//join player to battleground group
			currentBg->EventPlayerLoggedIn(this);
			currentBg->AddOrSetPlayerToCorrectBgGroup(this, m_bgData.bgTeam);

			SetInviteForBattlegroundQueueType(bgQueueTypeId,
					currentBg->GetInstanceID());
		}
		// Bg was not found - go to Entry Point
		else {
			// leave bg
			if (player_at_bg)
				currentBg->RemovePlayerAtLeave(GetGUID(), false, true);

			// Do not look for instance if bg not found
			const WorldLocation& _loc = GetBattlegroundEntryPoint();
			mapId = _loc.GetMapId();
			instanceId = 0;

			if (mapId == MAPID_INVALID) // Battleground Entry Point not found (???)
			{
				sLog->outError(
						"Player (guidlow %d) was in BG in database, but BG was not found, and entry point was invalid! Teleport to default race/class locations.",
						guid);
				RelocateToHomebind();
			} else
				Relocate(&_loc);

			// We are not in BG anymore
			m_bgData.bgInstanceID = 0;
		}
	}
	// currently we do not support transport in bg
	else if (transGUID) {
		m_movementInfo.t_guid =
				MAKE_NEW_GUID(transGUID, 0, HIGHGUID_MO_TRANSPORT);
		m_movementInfo.t_pos.Relocate(fields[26].GetFloat(),
				fields[27].GetFloat(), fields[28].GetFloat(),
				fields[29].GetFloat());

		if (!Trinity::IsValidMapCoord(
				GetPositionX() + m_movementInfo.t_pos.m_positionX,
				GetPositionY() + m_movementInfo.t_pos.m_positionY,
				GetPositionZ() + m_movementInfo.t_pos.m_positionZ,
				GetOrientation() + m_movementInfo.t_pos.m_orientation) ||
		// transport size limited
				m_movementInfo.t_pos.m_positionX > 250
				|| m_movementInfo.t_pos.m_positionY > 250
				|| m_movementInfo.t_pos.m_positionZ > 250) {
			sLog->outError(
					"Player (guidlow %d) have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to bind location.",
					guid, GetPositionX() + m_movementInfo.t_pos.m_positionX,
					GetPositionY() + m_movementInfo.t_pos.m_positionY,
					GetPositionZ() + m_movementInfo.t_pos.m_positionZ,
					GetOrientation() + m_movementInfo.t_pos.m_orientation);

			RelocateToHomebind();
		} else {
			for (MapManager::TransportSet::iterator iter =
					sMapMgr->m_Transports.begin();
					iter != sMapMgr->m_Transports.end(); ++iter) {
				if ((*iter)->GetGUIDLow() == transGUID) {
					m_transport = *iter;
					m_transport->AddPassenger(this);
					mapId = (m_transport->GetMapId());
					break;
				}
			}
			if (!m_transport) {
				sLog->outError(
						"Player (guidlow %d) have problems with transport guid (%u). Teleport to bind location.",
						guid, transGUID);

				RelocateToHomebind();
			}
		}
	}
	// currently we do not support taxi in instance
	else if (!taxi_nodes.empty()) {
		instanceId = 0;

		// Not finish taxi flight path
		if (m_bgData.HasTaxiPath()) {
			for (int i = 0; i < 2; ++i)
				m_taxi.AddTaxiDestination(m_bgData.taxiPath[i]);
		} else if (!m_taxi.LoadTaxiDestinationsFromString(taxi_nodes,
				GetTeam())) {
			// problems with taxi path loading
			TaxiNodesEntry const* nodeEntry = NULL;
			if (uint32 node_id = m_taxi.GetTaxiSource())
				nodeEntry = sTaxiNodesStore.LookupEntry(node_id);

			if (!nodeEntry) // don't know taxi start node, to homebind
			{
				sLog->outError(
						"Character %u have wrong data in taxi destination list, teleport to homebind.",
						GetGUIDLow());
				RelocateToHomebind();
			} else // have start node, to it
			{
				sLog->outError(
						"Character %u have too short taxi destination list, teleport to original node.",
						GetGUIDLow());
				mapId = nodeEntry->map_id;
				Relocate(nodeEntry->x, nodeEntry->y, nodeEntry->z, 0.0f);
			}
			m_taxi.ClearTaxiDestinations();
		}

		if (uint32 node_id = m_taxi.GetTaxiSource()) {
			// save source node as recall coord to prevent recall and fall from sky
			TaxiNodesEntry const* nodeEntry = sTaxiNodesStore.LookupEntry(
					node_id);
			if (nodeEntry && nodeEntry->map_id == GetMapId()) {
				ASSERT(nodeEntry); // checked in m_taxi.LoadTaxiDestinationsFromString
				mapId = nodeEntry->map_id;
				Relocate(nodeEntry->x, nodeEntry->y, nodeEntry->z, 0.0f);
			}

			// flight will started later
		}
	}

	// Map could be changed before
	mapEntry = sMapStore.LookupEntry(mapId);
	// client without expansion support
	if (mapEntry) {
		if (GetSession()->Expansion() < mapEntry->Expansion()) {
			sLog->outDebug(
					LOG_FILTER_PLAYER_LOADING,
					"Player %s using client without required expansion tried login at non accessible map %u",
					GetName(), mapId);
			RelocateToHomebind();
		}

		// fix crash (because of if (Map *map = _FindMap(instanceId)) in MapInstanced::CreateInstance)
		if (instanceId)
			if (InstanceSave * save = GetInstanceSave(mapId, mapEntry->IsRaid()))
				if (save->GetInstanceId() != instanceId)
					instanceId = 0;
	}

	// NOW player must have valid map
	// load the player's map here if it's not already loaded
	Map *map = sMapMgr->CreateMap(mapId, this, instanceId);

	if (!map) {
		instanceId = 0;
		AreaTrigger const* at = sObjectMgr->GetGoBackTrigger(mapId);
		if (at) {
			sLog->outError(
					"Player (guidlow %d) is teleported to gobacktrigger (Map: %u X: %f Y: %f Z: %f O: %f).",
					guid, mapId, GetPositionX(), GetPositionY(), GetPositionZ(),
					GetOrientation());
			Relocate(at->target_X, at->target_Y, at->target_Z,
					GetOrientation());
			mapId = at->target_mapId;
		} else {
			sLog->outError(
					"Player (guidlow %d) is teleported to home (Map: %u X: %f Y: %f Z: %f O: %f).",
					guid, mapId, GetPositionX(), GetPositionY(), GetPositionZ(),
					GetOrientation());
			RelocateToHomebind();
		}

		map = sMapMgr->CreateMap(mapId, this, 0);
		if (!map) {
			PlayerInfo const *info = sObjectMgr->GetPlayerInfo(getRace(),
					getClass());
			mapId = info->mapId;
			Relocate(info->positionX, info->positionY, info->positionZ, 0.0f);
			sLog->outError(
					"Player (guidlow %d) have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",
					guid, GetPositionX(), GetPositionY(), GetPositionZ(),
					GetOrientation());
			map = sMapMgr->CreateMap(mapId, this, 0);
			if (!map) {
				sLog->outError(
						"Player (guidlow %d) has invalid default map coordinates (X: %f Y: %f Z: %f O: %f). or instance couldn't be created",
						guid, GetPositionX(), GetPositionY(), GetPositionZ(),
						GetOrientation());
				return false;
			}
		}
	}

	// if the player is in an instance and it has been reset in the meantime teleport him to the entrance
	if (instanceId && !sInstanceSaveMgr->GetInstanceSave(instanceId)) {
		AreaTrigger const* at = sObjectMgr->GetMapEntranceTrigger(mapId);
		if (at)
			Relocate(at->target_X, at->target_Y, at->target_Z,
					at->target_Orientation);
		else {
			sLog->outError(
					"Player %s(GUID: %u) logged in to a reset instance (map: %u) and there is no area-trigger leading to this map. Thus he can't be ported back to the entrance. This _might_ be an exploit attempt.",
					GetName(), GetGUIDLow(), mapId);
			RelocateToHomebind();
		}
	}

	SetMap(map);
	StoreRaidMapDifficulty();

	// randomize first save time in range [CONFIG_INTERVAL_SAVE] around [CONFIG_INTERVAL_SAVE]
	// this must help in case next save after mass player load after server startup
	m_nextSave = urand(m_nextSave / 2, m_nextSave * 3 / 2);

	SaveRecallPosition();

	time_t now = time(NULL);
	time_t logoutTime = time_t(fields[22].GetUInt32());

	// since last logout (in seconds)
	uint32 time_diff = uint32(now - logoutTime); //uint64 is excessive for a time_diff in seconds.. uint32 allows for 136~ year difference.

	// set value, including drunk invisibility detection
	// calculate sobering. after 15 minutes logged out, the player will be sober again
	float soberFactor;
	if (time_diff > 15 * MINUTE)
		soberFactor = 0;
	else
		soberFactor = 1 - time_diff / (15.0f * MINUTE);
	uint16 newDrunkenValue = uint16(
			soberFactor * (GetUInt32Value(PLAYER_BYTES_3) & 0xFFFE));
	SetDrunkValue(newDrunkenValue);

	m_cinematic = fields[18].GetUInt8();
	m_Played_time[PLAYED_TIME_TOTAL] = fields[19].GetUInt32();
	m_Played_time[PLAYED_TIME_LEVEL] = fields[20].GetUInt32();

	m_resetTalentsCost = fields[24].GetUInt32();
	m_resetTalentsTime = time_t(fields[25].GetUInt32());

	m_taxi.LoadTaxiMask(fields[17].GetCString()); // must be before InitTaxiNodesForLevel

	uint32 extraflags = fields[31].GetUInt16();

	m_atLoginFlags = fields[33].GetUInt16();

	// Honor system
	// Update Honor kills data
	m_lastHonorUpdateTime = logoutTime;
	UpdateHonorFields();

	m_deathExpireTime = time_t(fields[36].GetUInt32());
	if (m_deathExpireTime > now + MAX_DEATH_COUNT * DEATH_EXPIRE_STEP)
		m_deathExpireTime = now + MAX_DEATH_COUNT * DEATH_EXPIRE_STEP - 1;

	// clear channel spell data (if saved at channel spell casting)
	SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, 0);
	SetUInt32Value(UNIT_CHANNEL_SPELL, 0);

	// clear charm/summon related fields
	SetUInt64Value(UNIT_FIELD_SUMMONEDBY, 0);
	SetUInt64Value(UNIT_FIELD_CHARMEDBY, 0);
	SetUInt64Value(UNIT_FIELD_CHARM, 0);
	SetUInt64Value(UNIT_FIELD_SUMMON, 0);
	SetUInt64Value(PLAYER_FARSIGHT, 0);
	SetCreatorGUID(0);

	RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FORCE_MOVE);

	// reset some aura modifiers before aura apply
	SetUInt32Value(PLAYER_TRACK_CREATURES, 0);
	SetUInt32Value(PLAYER_TRACK_RESOURCES, 0);

	// make sure the unit is considered out of combat for proper loading
	ClearInCombat();

	// make sure the unit is considered not in duel for proper loading
	SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
	SetUInt32Value(PLAYER_DUEL_TEAM, 0);

	// reset stats before loading any modifiers
	InitStatsForLevel();
	InitGlyphsForLevel();
	InitTaxiNodesForLevel();
	InitRunes();

	// rest bonus can only be calculated after InitStatsForLevel()
	m_rest_bonus = fields[21].GetFloat();

	if (time_diff > 0) {
		//speed collect rest bonus in offline, in logout, far from tavern, city (section/in hour)
		float bubble0 = 0.031f;
		//speed collect rest bonus in offline, in logout, in tavern, city (section/in hour)
		float bubble1 = 0.125f;
		float bubble =
				fields[23].GetUInt8() > 0 ?
						bubble1
								* sWorld->getRate(
										RATE_REST_OFFLINE_IN_TAVERN_OR_CITY) :
						bubble0
								* sWorld->getRate(
										RATE_REST_OFFLINE_IN_WILDERNESS);

		SetRestBonus(
				GetRestBonus()
						+ time_diff
								* ((float) GetUInt32Value(PLAYER_NEXT_LEVEL_XP)
										/ 72000) * bubble);
	}

	// load skills after InitStatsForLevel because it triggering aura apply also
	_LoadSkills(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADSKILLS));
	UpdateSkillsForLevel(); //update skills after load, to make sure they are correctly update at player load

	// apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()

	//mails are loaded only when needed ;-) - when player in game click on mailbox.
	//_LoadMail();

	m_specsCount = fields[62].GetUInt8();
	m_activeSpec = fields[63].GetUInt8();

	// sanity check
	if (m_specsCount > MAX_TALENT_SPECS || m_activeSpec > MAX_TALENT_SPEC
			|| m_specsCount < MIN_TALENT_SPECS) {
		m_activeSpec = 0;
		sLog->outError(
				"Player %s(GUID: %u) has SpecCount = %u and ActiveSpec = %u.",
				GetName(), GetGUIDLow(), m_specsCount, m_activeSpec);
	}

	_LoadCurrency(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CURRENCY));
	_LoadConquestPointsWeekCap(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CP_WEEK_CAP));
	_LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADTALENTS));
	_LoadTalentBranchSpecs(
			holder->GetPreparedResult(
					PLAYER_LOGIN_QUERY_LOADTALENTBRANCHSPECS));
	_LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADSPELLS));

	_LoadGlyphs(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADGLYPHS));
	_LoadAuras(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADAURAS),
			time_diff);
	_LoadGlyphAuras();
	// add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura)
	if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
		m_deathState = DEAD;

	// after spell load, learn rewarded spell if need also
	_LoadQuestStatus(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS));
	_LoadQuestStatusRewarded(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUSREW));
	_LoadDailyQuestStatus(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS));
	_LoadWeeklyQuestStatus(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADWEKLYQUESTSTATUS));
	_LoadRandomBGStatus(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADRANDOMBG));

	// after spell and quest load
	InitTalentForLevel();
	learnDefaultSpells();

	// must be before inventory (some items required reputation check)
	m_reputationMgr.LoadFromDB(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADREPUTATION));

	_LoadInventory(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADINVENTORY),
			time_diff);

	// update items with duration and realtime
	UpdateItemDuration(time_diff, true);

	_LoadActions(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADACTIONS));

	// unread mails and next delivery time, actual mails not loaded
	_LoadMailInit(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADMAILCOUNT),
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADMAILDATE));

	m_social = sSocialMgr->LoadFromDB(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADSOCIALLIST),
			GetGUIDLow());

	// check PLAYER_CHOSEN_TITLE compatibility with PLAYER__FIELD_KNOWN_TITLES
	// note: PLAYER__FIELD_KNOWN_TITLES updated at quest status loaded
	uint32 curTitle = fields[46].GetUInt32();
	if (curTitle && !HasTitle(curTitle))
		curTitle = 0;

	SetUInt32Value(PLAYER_CHOSEN_TITLE, curTitle);

	// has to be called after last Relocate() in Player::LoadFromDB
	SetFallInformation(0, GetPositionZ());

	_LoadSpellCooldowns(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS));

	// Spell code allow apply any auras to dead character in load time in aura/spell/item loading
	// Do now before stats re-calculation cleanup for ghost state unexpected auras
	if (!isAlive())
		RemoveAllAurasOnDeath();
	else
		RemoveAllAurasRequiringDeadTarget();

	//apply all stat bonuses from items and auras
	SetCanModifyStats(true);
	UpdateAllStats();

	// restore remembered power/health values (but not more max values)
	uint32 savedHealth = fields[50].GetUInt32();
	SetHealth(savedHealth > GetMaxHealth() ? GetMaxHealth() : savedHealth);
	for (uint8 i = 0; i < MAX_POWERS; ++i) {
		uint32 savedPower = fields[51 + i].GetUInt32();
		SetPower(
				Powers(i),
				savedPower > GetMaxPower(Powers(i)) ?
						GetMaxPower(Powers(i)) : savedPower);
	}

	sLog->outDebug(LOG_FILTER_PLAYER_LOADING,
			"The value of player %s after load item and aura is: ",
			m_name.c_str());
	outDebugValues();

	// GM state
	if (GetSession()->GetSecurity() > SEC_PLAYER) {
		switch (sWorld->getIntConfig(CONFIG_GM_LOGIN_STATE)) {
		default:
		case 0:
			break; // disable
		case 1:
			SetGameMaster(true);
			break; // enable
		case 2: // save state
			if (extraflags & PLAYER_EXTRA_GM_ON)
				SetGameMaster(true);
			break;
		}

		switch (sWorld->getIntConfig(CONFIG_GM_VISIBLE_STATE)) {
		default:
		case 0:
			SetGMVisible(false);
			break; // invisible
		case 1:
			break; // visible
		case 2: // save state
			if (extraflags & PLAYER_EXTRA_GM_INVISIBLE)
				SetGMVisible(false);
			break;
		}

		/*switch(sWorld->getIntConfig(CONFIG_GM_ACCEPT_TICKETS))
		 {
		 default:
		 case 0:                        break;           // disable
		 case 1: SetAcceptTicket(true); break;           // enable
		 case 2:                                         // save state
		 if (extraflags & PLAYER_EXTRA_GM_ACCEPT_TICKETS)
		 SetAcceptTicket(true);
		 break;
		 }*/

		switch (sWorld->getIntConfig(CONFIG_GM_CHAT)) {
		default:
		case 0:
			break; // disable
		case 1:
			SetGMChat(true);
			break; // enable
		case 2: // save state
			if (extraflags & PLAYER_EXTRA_GM_CHAT)
				SetGMChat(true);
			break;
		}

		switch (sWorld->getIntConfig(CONFIG_GM_WHISPERING_TO)) {
		default:
		case 0:
			break; // disable
		case 1:
			SetAcceptWhispers(true);
			break; // enable
		case 2: // save state
			if (extraflags & PLAYER_EXTRA_ACCEPT_WHISPERS)
				SetAcceptWhispers(true);
			break;
		}
	}

	//TODO: find why the player is not in worgen form at login with this code.
	//if (extraflags & PLAYER_EXTRA_WORGEN_FORM)
	//    setInWorgenForm(IN_WORGEN_FORM);

	_LoadDeclinedNames(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES));

	m_achievementMgr.LoadFromDB(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS),
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS));
	m_achievementMgr.CheckAllAchievementCriteria();

	_LoadEquipmentSets(
			holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS));

	return true;
}

bool Player::isAllowedToLoot(const Creature* creature) {
	if (!creature->isDead() || !creature->IsDamageEnoughForLootingAndReward())
		return false;

	const Loot* loot = &creature->loot;
	if (loot->isLooted()) // nothing to loot or everything looted.
		return false;

	Group* thisGroup = GetGroup();
	if (!thisGroup)
		return this == creature->GetLootRecipient();
	else if (thisGroup != creature->GetLootRecipientGroup())
		return false;

	switch (thisGroup->GetLootMethod()) {
	case FREE_FOR_ALL:
		return true;
	case ROUND_ROBIN:
	case MASTER_LOOT:
		// may only loot if the player is the loot roundrobin player
		// or if there are free/quest/conditional item for the player
		if (loot->roundRobinPlayer == 0 || loot->roundRobinPlayer == GetGUID())
			return true;

		return loot->hasItemFor(this);
	case GROUP_LOOT:
	case NEED_BEFORE_GREED:
		// may only loot if the player is the loot roundrobin player
		// or item over threshold (so roll(s) can be launched)
		// or if there are free/quest/conditional item for the player
		if (loot->roundRobinPlayer == 0 || loot->roundRobinPlayer == GetGUID())
			return true;

		if (loot->hasOverThresholdItem())
			return true;

		return loot->hasItemFor(this);
	}

	return false;
}

void Player::_LoadActions(PreparedQueryResult result) {
	m_actionButtons.clear();

	if (result) {
		do {
			Field* fields = result->Fetch();
			uint8 button = fields[0].GetUInt8();
			uint32 action = fields[1].GetUInt32();
			uint8 type = fields[2].GetUInt8();

			if (ActionButton* ab = addActionButton(button, action, type))
				ab->uState = ACTIONBUTTON_UNCHANGED;
			else {
				sLog->outError("  ...at loading, and will deleted in DB also");

				// Will deleted in DB at next save (it can create data until save but marked as deleted)
				m_actionButtons[button].uState = ACTIONBUTTON_DELETED;
			}
		} while (result->NextRow());
	}
}

void Player::_LoadAuras(PreparedQueryResult result, uint32 timediff) {
	sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Loading auras for player %u",
			GetGUIDLow());

	/*                                                           0       1        2         3                 4         5      6       7         8              9            10
	 QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid, spell, effect_mask, recalculate_mask, stackcount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2,
	 11          12          13
	 maxduration, remaintime, remaincharges FROM character_aura WHERE guid = '%u'", GetGUIDLow());
	 */

	if (result) {
		do {
			Field* fields = result->Fetch();
			int32 damage[3];
			int32 baseDamage[3];
			uint64 caster_guid = fields[0].GetUInt64();
			uint32 spellid = fields[1].GetUInt32();
			uint8 effmask = fields[2].GetUInt8();
			uint8 recalculatemask = fields[3].GetUInt8();
			uint8 stackcount = fields[4].GetUInt8();
			damage[0] = fields[5].GetInt32();
			damage[1] = fields[6].GetInt32();
			damage[2] = fields[7].GetInt32();
			baseDamage[0] = fields[8].GetInt32();
			baseDamage[1] = fields[9].GetInt32();
			baseDamage[2] = fields[10].GetInt32();
			int32 maxduration = fields[11].GetInt32();
			int32 remaintime = fields[12].GetInt32();
			uint8 remaincharges = fields[13].GetUInt8();

			SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid);
			if (!spellproto) {
				sLog->outError("Unknown aura (spellid %u), ignore.", spellid);
				continue;
			}

			// negative effects should continue counting down after logout
			if (remaintime != -1 && !IsPositiveSpell(spellid)) {
				if (remaintime / IN_MILLISECONDS <= int32(timediff))
					continue;

				remaintime -= timediff * IN_MILLISECONDS;
			}

			// prevent wrong values of remaincharges
			if (spellproto->procCharges) {
				if (remaincharges <= 0
						|| remaincharges > spellproto->procCharges)
					remaincharges = spellproto->procCharges;
			} else
				remaincharges = 0;

			if (Aura * aura = Aura::TryCreate(spellproto, effmask, this, NULL, &baseDamage[0], NULL, caster_guid)) {
				if (!aura->CanBeSaved()) {
					aura->Remove();
					continue;
				}
				aura->SetLoadedState(maxduration, remaintime, remaincharges,
						stackcount, recalculatemask, &damage[0]);
				aura->ApplyForTargets();
				sLog->outDetail("Added aura spellid %u, effectmask %u",
						spellproto->Id, effmask);
			}
		} while (result->NextRow());
	}
}

void Player::_LoadGlyphAuras() {
	for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i) {
		if (uint32 glyph = GetGlyph(i)) {
			if (GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyph)) {
				if (GlyphSlotEntry const *gs = sGlyphSlotStore.LookupEntry(GetGlyphSlot(i))) {
					if (gp->TypeFlags == gs->TypeFlags) {
						CastSpell(this, gp->SpellId, true);
						continue;
					} else
						sLog->outError(
								"Player %s has glyph with typeflags %u in slot with typeflags %u, removing.",
								m_name.c_str(), gp->TypeFlags, gs->TypeFlags);
				} else
					sLog->outError(
							"Player %s has not existing glyph slot entry %u on index %u",
							m_name.c_str(), GetGlyphSlot(i), i);
			} else
				sLog->outError(
						"Player %s has not existing glyph entry %u on index %u",
						m_name.c_str(), glyph, i);

			// On any error remove glyph
			SetGlyph(i, 0);
		}
	}
}

void Player::LoadCorpse() {
	if (isAlive())
		sObjectAccessor->ConvertCorpseForPlayer(GetGUID());
	else {
		if (Corpse *corpse = GetCorpse())
			ApplyModFlag(
					PLAYER_FIELD_BYTES,
					PLAYER_FIELD_BYTE_RELEASE_TIMER,
					corpse
							&& !sMapStore.LookupEntry(corpse->GetMapId())->Instanceable());
		else
			//Prevent Dead Player login without corpse
			ResurrectPlayer(0.5f);
	}
}

void Player::_LoadInventory(PreparedQueryResult result, uint32 timediff) {
	//QueryResult *result = CharacterDatabase.PQuery("SELECT data, text, bag, slot, item, item_template FROM character_inventory JOIN item_instance ON character_inventory.item = item_instance.guid WHERE character_inventory.guid = '%u' ORDER BY bag, slot", GetGUIDLow());
	std::map<uint64, Bag*> bagMap; // fast guid lookup for bags
	//NOTE: the "order by `bag`" is important because it makes sure
	//the bagMap is filled before items in the bags are loaded
	//NOTE2: the "order by `slot`" is needed because mainhand weapons are (wrongly?)
	//expected to be equipped before offhand items (TODO: fixme)

	uint32 zone = GetZoneId();

	if (result) {
		std::list<Item*> problematicItems;
		SQLTransaction trans = CharacterDatabase.BeginTransaction();

		// prevent items from being added to the queue when stored
		m_itemUpdateQueueBlocked = true;
		do {
			Field* fields = result->Fetch();

			uint32 bag_guid = fields[11].GetUInt32();
			uint8 slot = fields[12].GetUInt8();
			uint32 item_guid = fields[13].GetUInt32();
			uint32 item_id = fields[14].GetUInt32();

			ItemPrototype const * proto = ObjectMgr::GetItemPrototype(item_id);

			if (!proto) {
				PreparedStatement* stmt =
						CharacterDatabase.GetPreparedStatement(
								CHAR_DEL_INVENTORY_ITEM);
				stmt->setUInt32(0, item_guid);
				trans->Append(stmt);
				stmt = CharacterDatabase.GetPreparedStatement(
						CHAR_DEL_ITEM_INSTANCE);
				stmt->setUInt32(0, item_guid);
				trans->Append(stmt);
				sLog->outError(
						"Player::_LoadInventory: Player %s has an unknown item (id: #%u) in inventory, deleted.",
						GetName(), item_id);
				continue;
			}

			Item *item = NewItemOrBag(proto);

			if (!item->LoadFromDB(item_guid, GetGUID(), fields, item_id)) {
				sLog->outError(
						"Player::_LoadInventory: Player %s has broken item (id: #%u) in inventory, deleted.",
						GetName(), item_id);
				PreparedStatement* stmt =
						CharacterDatabase.GetPreparedStatement(
								CHAR_DEL_INVENTORY_ITEM);
				stmt->setUInt32(0, item_guid);
				trans->Append(stmt);
				item->FSetState(ITEM_REMOVED);
				item->SaveToDB(trans); // it also deletes item object !
				continue;
			}

			// not allow have in alive state item limited to another map/zone
			if (isAlive()
					&& item->IsLimitedToAnotherMapOrZone(GetMapId(), zone)) {
				PreparedStatement* stmt =
						CharacterDatabase.GetPreparedStatement(
								CHAR_DEL_INVENTORY_ITEM);
				stmt->setUInt32(0, item_guid);
				trans->Append(stmt);
				item->FSetState(ITEM_REMOVED);
				item->SaveToDB(trans); // it also deletes item object !
				continue;
			}

			// "Conjured items disappear if you are logged out for more than 15 minutes"
			if (timediff > 15 * MINUTE
					&& proto->Flags & ITEM_PROTO_FLAG_CONJURED) {
				PreparedStatement* stmt =
						CharacterDatabase.GetPreparedStatement(
								CHAR_DEL_INVENTORY_ITEM);
				stmt->setUInt32(0, item_guid);
				trans->Append(stmt);
				item->FSetState(ITEM_REMOVED);
				item->SaveToDB(trans); // it also deletes item object !
				continue;
			}

			if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE)) {
				if (item->GetPlayedTime() > (2 * HOUR)) {
					sLog->outDebug(
							LOG_FILTER_PLAYER_LOADING,
							"Item::LoadFromDB, Item GUID: %u: refund time expired, deleting refund data and removing refundable flag.",
							item->GetGUIDLow());
					trans->PAppend(
							"DELETE FROM item_refund_instance WHERE item_guid = '%u'",
							item->GetGUIDLow());
					item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE);
				} else {
					PreparedStatement* stmt =
							CharacterDatabase.GetPreparedStatement(
									CHAR_LOAD_ITEM_REFUNDS);
					stmt->setUInt32(0, item->GetGUIDLow());
					stmt->setUInt32(1, GetGUIDLow());
					PreparedQueryResult result2 = CharacterDatabase.Query(stmt);
					if (!result2) {
						sLog->outDebug(
								LOG_FILTER_PLAYER_LOADING,
								"Item::LoadFromDB, Item GUID: %u has field flags & ITEM_FLAGS_REFUNDABLE but has no data in item_refund_instance, removing flag.",
								item->GetGUIDLow());
						item->RemoveFlag(ITEM_FIELD_FLAGS,
								ITEM_FLAG_REFUNDABLE);
					} else {
						Field* fields2 = result2->Fetch();
						item->SetRefundRecipient(fields2[0].GetUInt32());
						item->SetPaidMoney(fields2[1].GetUInt32());
						item->SetPaidExtendedCost(fields2[2].GetUInt16());
						AddRefundReference(item->GetGUIDLow());
					}
				}
			} else if (item->HasFlag(ITEM_FIELD_FLAGS,
					ITEM_FLAG_BOP_TRADEABLE)) {
				PreparedStatement* stmt =
						CharacterDatabase.GetPreparedStatement(
								CHAR_LOAD_ITEM_BOP_TRADE);
				stmt->setUInt32(0, item->GetGUIDLow());
				PreparedQueryResult result2 = CharacterDatabase.Query(stmt);
				if (!result2) {
					sLog->outDebug(
							LOG_FILTER_PLAYER_LOADING,
							"Item::LoadFromDB, Item GUID: %u has flag ITEM_FLAG_BOP_TRADEABLE but has no data in item_soulbound_trade_data, removing flag.",
							item->GetGUIDLow());
					item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_BOP_TRADEABLE);
				} else {
					Field* fields2 = result2->Fetch();
					std::string strGUID = fields2[0].GetString();
					Tokens GUIDlist(strGUID, ' ');
					AllowedLooterSet looters;
					for (Tokens::iterator itr = GUIDlist.begin();
							itr != GUIDlist.end(); ++itr)
						looters.insert(atol(*itr));
					item->SetSoulboundTradeable(&looters, this, true);
					m_itemSoulboundTradeable.push_back(item);
				}
			}

			bool success = true;

			if (!bag_guid) {
				// the item is not in a bag
				item->SetContainer(NULL);
				item->SetSlot(slot);

				if (IsInventoryPos(INVENTORY_SLOT_BAG_0, slot)) {
					ItemPosCountVec dest;
					if (CanStoreItem(INVENTORY_SLOT_BAG_0, slot, dest, item,
							false) == EQUIP_ERR_OK)
						item = StoreItem(dest, item, true);
					else
						success = false;
				} else if (IsEquipmentPos(INVENTORY_SLOT_BAG_0, slot)) {
					uint16 dest;
					if (CanEquipItem(slot, dest, item, false, false)
							== EQUIP_ERR_OK)
						QuickEquipItem(dest, item);
					else
						success = false;
				} else if (IsBankPos(INVENTORY_SLOT_BAG_0, slot)) {
					ItemPosCountVec dest;
					if (CanBankItem(INVENTORY_SLOT_BAG_0, slot, dest, item,
							false, false) == EQUIP_ERR_OK)
						item = BankItem(dest, item, true);
					else
						success = false;
				}

				if (success) {
					// store bags that may contain items in them
					if (item->IsBag() && IsBagPos(item->GetPos()))
						bagMap[item_guid] = (Bag*) item;
				}
			} else {
				item->SetSlot(NULL_SLOT);
				// the item is in a bag, find the bag
				std::map<uint64, Bag*>::iterator itr = bagMap.find(bag_guid);
				if (itr != bagMap.end()) {
					ItemPosCountVec dest;
					uint8 result = CanStoreItem(itr->second->GetSlot(), slot,
							dest, item);
					if (result == EQUIP_ERR_OK)
						itr->second->StoreItem(slot, item, true);
					else {
						sLog->outError(
								"Player::_LoadInventory: Player %s has item (GUID: %u Entry: %u) can't be loaded to inventory (Bag GUID: %u Slot: %u) by reason %u.",
								GetName(), item_guid, item_id, bag_guid, slot,
								result);
						success = false;
					}
				} else
					success = false;
			}

			// item's state may have changed after stored
			if (success)
				item->SetState(ITEM_UNCHANGED, this);
			else {
				sLog->outError(
						"Player::_LoadInventory: Player %s has item (GUID: %u Entry: %u) can't be loaded to inventory (Bag GUID: %u Slot: %u) by some reason, will send by mail.",
						GetName(), item_guid, item_id, bag_guid, slot);
				PreparedStatement* stmt =
						CharacterDatabase.GetPreparedStatement(
								CHAR_DEL_INVENTORY_ITEM);
				stmt->setUInt32(0, item_guid);
				trans->Append(stmt);
				problematicItems.push_back(item);
			}
		} while (result->NextRow());

		m_itemUpdateQueueBlocked = false;

		// send by mail problematic items
		while (!problematicItems.empty()) {
			std::string subject = GetSession()->GetArkCoreString(
					LANG_NOT_EQUIPPED_ITEM);

			// fill mail
			MailDraft draft(subject,
					"There's were problems with equipping item(s).");

			for (uint8 i = 0; !problematicItems.empty() && i < MAX_MAIL_ITEMS;
					++i) {
				Item* item = problematicItems.front();
				problematicItems.pop_front();

				draft.AddItem(item);
			}

			draft.SendMailTo(trans, this, MailSender(this, MAIL_STATIONERY_GM),
					MAIL_CHECK_MASK_COPIED);
		}
		CharacterDatabase.CommitTransaction(trans);
	}
	//if (isAlive())
	_ApplyAllItemMods();
}

// load mailed item which should receive current player
void Player::_LoadMailedItems(Mail *mail) {
	// data needs to be at first place for Item::LoadFromDB
	PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(
			CHAR_LOAD_PLAYER_MAILITEMS);
	stmt->setUInt32(0, mail->messageID);
	PreparedQueryResult result = CharacterDatabase.Query(stmt);
	if (!result)
		return;

	SQLTransaction trans = CharacterDatabase.BeginTransaction();
	do {
		Field* fields = result->Fetch();

		uint32 item_guid_low = fields[11].GetUInt32();
		uint32 item_template = fields[12].GetUInt32();

		mail->AddItem(item_guid_low, item_template);

		ItemPrototype const *proto = ObjectMgr::GetItemPrototype(item_template);

		if (!proto) {
			sLog->outError(
					"Player %u has unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.",
					GetGUIDLow(), item_guid_low, item_template,
					mail->messageID);
			trans->PAppend("DELETE FROM mail_items WHERE item_guid = '%u'",
					item_guid_low);
			PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(
					CHAR_DEL_ITEM_INSTANCE);
			stmt->setUInt32(0, item_guid_low);
			trans->Append(stmt);
			continue;
		}

		Item *item = NewItemOrBag(proto);

		if (!item->LoadFromDB(item_guid_low,
				MAKE_NEW_GUID(fields[13].GetUInt32(), 0, HIGHGUID_PLAYER),
				fields, item_template)) {
			sLog->outError(
					"Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail",
					mail->messageID, item_guid_low);
			CharacterDatabase.PExecute(
					"DELETE FROM mail_items WHERE item_guid = '%u'",
					item_guid_low);
			item->FSetState(ITEM_REMOVED);
			item->SaveToDB(trans); // it also deletes item object !
			continue;
		}

		AddMItem(item);
	} while (result->NextRow());

	CharacterDatabase.CommitTransaction(trans);
}

void Player::_LoadMailInit(PreparedQueryResult resultUnread,
		PreparedQueryResult resultDelivery) {
	//set a count of unread mails
	//QueryResult *resultMails = CharacterDatabase.PQuery("SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" UI64FMTD "'", GUID_LOPART(playerGuid), (uint64)cTime);
	if (resultUnread)
		unReadMails = (*resultUnread)[0].GetUInt8();

	// store nearest delivery time (it > 0 and if it < current then at next player update SendNewMaill will be called)
	//resultMails = CharacterDatabase.PQuery("SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", GUID_LOPART(playerGuid));
	if (resultDelivery)
		m_nextMailDelivereTime = (time_t)(*resultDelivery)[0].GetUInt32();
}

void Player::_LoadMail() {
	m_mail.clear();
	//mails are in right order                             0  1           2      3        4       5          6         7           8            9     10  11      12         13
	QueryResult result =
			CharacterDatabase.PQuery(
					"SELECT id, messageType, sender, receiver, subject, body, has_items, expire_time, deliver_time, money, cod, checked, stationery, mailTemplateId FROM mail WHERE receiver = '%u' ORDER BY id DESC",
					GetGUIDLow());
	if (result) {
		do {
			Field *fields = result->Fetch();
			Mail *m = new Mail;
			m->messageID = fields[0].GetUInt32();
			m->messageType = fields[1].GetUInt8();
			m->sender = fields[2].GetUInt32();
			m->receiver = fields[3].GetUInt32();
			m->subject = fields[4].GetString();
			m->body = fields[5].GetString();
			bool has_items = fields[6].GetBool();
			m->expire_time = time_t(fields[7].GetUInt32());
			m->deliver_time = time_t(fields[8].GetUInt32());
			m->money = fields[9].GetUInt32();
			m->COD = fields[10].GetUInt32();
			m->checked = fields[11].GetUInt32();
			m->stationery = fields[12].GetUInt8();
			m->mailTemplateId = fields[13].GetInt16();

			if (m->mailTemplateId
					&& !sMailTemplateStore.LookupEntry(m->mailTemplateId)) {
				sLog->outError(
						"Player::_LoadMail - Mail (%u) have not existed MailTemplateId (%u), remove at load",
						m->messageID, m->mailTemplateId);
				m->mailTemplateId = 0;
			}

			m->state = MAIL_STATE_UNCHANGED;

			if (has_items)
				_LoadMailedItems(m);

			m_mail.push_back(m);
		} while (result->NextRow());
	}
	m_mailsLoaded = true;
}

void Player::LoadPet() {
	//fixme: the pet should still be loaded if the player is not in world
	// just not added to the map
	if (IsInWorld()) {
		Pet *pet = new Pet(this);
		if (!pet->LoadPetFromDB(this, 0, 0, true, PET_SLOT_ACTUAL_PET_SLOT))
			delete pet;
	}
}

void Player::_LoadQuestStatus(PreparedQueryResult result) {
	uint16 slot = 0;

	////                                                       0      1       2        3        4           5          6         7           8           9           10
	//QueryResult *result = CharacterDatabase.PQuery("SELECT quest, status, explored, timer, mobcount1, mobcount2, mobcount3, mobcount4, itemcount1, itemcount2, itemcount3,
	//                                                    11
	//                                                itemcount4 FROM character_queststatus WHERE guid = '%u'", GetGUIDLow());

	if (result) {
		do {
			Field* fields = result->Fetch();

			uint32 quest_id = fields[0].GetUInt32();
			// used to be new, no delete?
			Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest_id);
			if (pQuest) {
				// find or create
				QuestStatusData& questStatusData = m_QuestStatus[quest_id];

				uint8 qstatus = fields[1].GetUInt8();
				if (qstatus < MAX_QUEST_STATUS)
					questStatusData.m_status = QuestStatus(qstatus);
				else {
					questStatusData.m_status = QUEST_STATUS_INCOMPLETE;
					sLog->outError(
							"Player %s have invalid quest %d status (%u), replaced by QUEST_STATUS_INCOMPLETE(3).",
							GetName(), quest_id, qstatus);
				}

				questStatusData.m_explored = (fields[2].GetUInt8() > 0);

				time_t quest_time = time_t(fields[3].GetUInt32());

				if (pQuest->HasSpecialFlag(QUEST_SPECIAL_FLAG_TIMED)
						&& !GetQuestRewardStatus(quest_id)) {
					AddTimedQuest(quest_id);

					if (quest_time <= sWorld->GetGameTime())
						questStatusData.m_timer = 1;
					else
						questStatusData.m_timer = uint32(
								(quest_time - sWorld->GetGameTime())
										* IN_MILLISECONDS);
				} else
					quest_time = 0;

				questStatusData.m_creatureOrGOcount[0] = fields[4].GetUInt16();
				questStatusData.m_creatureOrGOcount[1] = fields[5].GetUInt16();
				questStatusData.m_creatureOrGOcount[2] = fields[6].GetUInt16();
				questStatusData.m_creatureOrGOcount[3] = fields[7].GetUInt16();
				questStatusData.m_itemcount[0] = fields[8].GetUInt16();
				questStatusData.m_itemcount[1] = fields[9].GetUInt16();
				questStatusData.m_itemcount[2] = fields[10].GetUInt16();
				questStatusData.m_itemcount[3] = fields[11].GetUInt16();

				// add to quest log
				if (slot < MAX_QUEST_LOG_SIZE
						&& questStatusData.m_status != QUEST_STATUS_NONE) {
					SetQuestSlot(slot, quest_id, uint32(quest_time)); // cast can't be helped

					if (questStatusData.m_status == QUEST_STATUS_COMPLETE)
						SetQuestSlotState(slot, QUEST_STATE_COMPLETE);
					else if (questStatusData.m_status == QUEST_STATUS_FAILED)
						SetQuestSlotState(slot, QUEST_STATE_FAIL);

					for (uint8 idx = 0; idx < QUEST_OBJECTIVES_COUNT; ++idx)
						if (questStatusData.m_creatureOrGOcount[idx])
							SetQuestSlotCounter(slot, idx,
									questStatusData.m_creatureOrGOcount[idx]);

					++slot;
				}

				sLog->outDebug(
						LOG_FILTER_PLAYER_LOADING,
						"Quest status is {%u} for quest {%u} for player (GUID: %u)",
						questStatusData.m_status, quest_id, GetGUIDLow());
			}
		} while (result->NextRow());
	}

	// clear quest log tail
	for (uint16 i = slot; i < MAX_QUEST_LOG_SIZE; ++i)
		SetQuestSlot(i, 0);
}

void Player::_LoadQuestStatusRewarded(PreparedQueryResult result) {
	// SELECT quest FROM character_queststatus_rewarded WHERE guid = ?

	if (result) {
		do {
			Field* fields = result->Fetch();

			uint32 quest_id = fields[0].GetUInt32();
			// used to be new, no delete?
			Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest_id);
			if (pQuest) {
				// learn rewarded spell if unknown
				learnQuestRewardedSpells(pQuest);

				// set rewarded title if any
				if (pQuest->GetCharTitleId()) {
					if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId()))
						SetTitle(titleEntry);
				}

				if (pQuest->GetBonusTalents())
					m_questRewardTalentCount += pQuest->GetBonusTalents();
			}

			m_RewardedQuests.insert(quest_id);
		} while (result->NextRow());
	}
}

void Player::_LoadDailyQuestStatus(PreparedQueryResult result) {
	for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS;
			++quest_daily_idx)
		SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx, 0);

	m_DFQuests.clear();

	//QueryResult *result = CharacterDatabase.PQuery("SELECT quest, time FROM character_queststatus_daily WHERE guid = '%u'", GetGUIDLow());

	if (result) {
		uint32 quest_daily_idx = 0;

		do {
			Field* fields = result->Fetch();
			if (Quest const* qQuest = sObjectMgr->GetQuestTemplate(fields[0].GetUInt32())) {
				if (qQuest->IsDFQuest()) {
					m_DFQuests.insert(qQuest->GetQuestId());
					m_lastDailyQuestTime = time_t(fields[1].GetUInt32());
					continue;
				}
			}

			if (quest_daily_idx >= PLAYER_MAX_DAILY_QUESTS) // max amount with exist data in query
			{
				sLog->outError(
						"Player (GUID: %u) have more 25 daily quest records in `charcter_queststatus_daily`",
						GetGUIDLow());
				break;
			}

			uint32 quest_id = fields[0].GetUInt32();

			// save _any_ from daily quest times (it must be after last reset anyway)
			m_lastDailyQuestTime = (time_t) fields[1].GetUInt32();

			Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest_id);
			if (!pQuest)
				continue;

			SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx,
					quest_id);
			++quest_daily_idx;

			sLog->outDebug(LOG_FILTER_PLAYER_LOADING,
					"Daily quest (%u) cooldown for player (GUID: %u)", quest_id,
					GetGUIDLow());
		} while (result->NextRow());
	}

	m_DailyQuestChanged = false;
}

void Player::_LoadWeeklyQuestStatus(PreparedQueryResult result) {
	m_weeklyquests.clear();

	if (result) {
		do {
			uint32 quest_id = (*result)[0].GetUInt32();
			Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest_id);
			if (!pQuest)
				continue;

			m_weeklyquests.insert(quest_id);
			sLog->outDebug(LOG_FILTER_PLAYER_LOADING,
					"Weekly quest {%u} cooldown for player (GUID: %u)",
					quest_id, GetGUIDLow());
		} while (result->NextRow());
	}

	m_WeeklyQuestChanged = false;
}

void Player::_LoadCurrency(PreparedQueryResult result) {
	//         0         1      2
	// "SELECT currency, count, thisweek FROM character_currency WHERE guid = '%u'"

	if (result) {
		do {
			Field *fields = result->Fetch();

			uint32 currency_id = fields[0].GetUInt16();
			uint32 totalCount = fields[1].GetUInt32();
			uint32 weekCount = fields[2].GetUInt32();

			const CurrencyTypesEntry* entry = sCurrencyTypesStore.LookupEntry(
					currency_id);
			if (!entry) {
				sLog->outError(
						"Player::_LoadCurrency: %s has not existing currency %u, removing.",
						GetName(), currency_id);
				CharacterDatabase.PExecute(
						"DELETE FROM character_currency WHERE currency = '%u'",
						currency_id);
				continue;
			}

			uint32 weekCap = _GetCurrencyWeekCap(entry);
			uint32 totalCap = _GetCurrencyTotalCap(entry);

			PlayerCurrency cur;

			cur.state = PLAYERCURRENCY_UNCHANGED;

			if (totalCap == 0) // unlimited, don't check
				cur.totalCount = totalCount;
			else
				cur.totalCount = totalCount > totalCap ? totalCap : totalCount;

			if (weekCap == 0)
				cur.weekCount = weekCount;
			else
				cur.weekCount = weekCount > weekCap ? weekCap : weekCount;

			m_currencies[currency_id] = cur;
		} while (result->NextRow());
	}
}

void Player::_LoadConquestPointsWeekCap(PreparedQueryResult result) {
	//           0         1            2
	// "SELECT source, maxWeekRating, weekCap FROM character_cp_weekcap WHERE guid = ?"

	if (result) {
		do {
			Field *fields = result->Fetch();

			uint16 source = fields[0].GetUInt16();
			if (source != CP_SOURCE_ARENA && source != CP_SOURCE_RATED_BG)
				continue;

			m_maxWeekRating[source] = fields[1].GetUInt16();
			m_conquestPointsWeekCap[source] = fields[2].GetUInt16();
		} while (result->NextRow());
	}
}

void Player::_LoadSpells(PreparedQueryResult result) {
	//QueryResult *result = CharacterDatabase.PQuery("SELECT spell, active, disabled FROM character_spell WHERE guid = '%u'", GetGUIDLow());

	if (result) {
		do
			addSpell((*result)[0].GetUInt32(), (*result)[1].GetBool(), false,
					false, (*result)[2].GetBool());
		while (result->NextRow());
	}
}

void Player::_LoadGroup(PreparedQueryResult result) {
	//QueryResult *result = CharacterDatabase.PQuery("SELECT guid FROM group_member WHERE memberGuid=%u", GetGUIDLow());
	if (result) {
		if (Group * group = sObjectMgr->GetGroupByGUID(
				(*result)[0].GetUInt32())) {
			uint8 subgroup = group->GetMemberGroup(GetGUID());
			SetGroup(group, subgroup);
			if (getLevel() >= LEVELREQUIREMENT_HEROIC) {
				// the group leader may change the instance difficulty while the player is offline
				SetDungeonDifficulty(group->GetDungeonDifficulty());
				SetRaidDifficulty(group->GetRaidDifficulty());
			}
		}
	}
}

void Player::_LoadBoundInstances(PreparedQueryResult result) {
	for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
		m_boundInstances[i].clear();

	Group *group = GetGroup();

	//QueryResult *result = CharacterDatabase.PQuery("SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid));
	if (result) {
		do {
			Field* fields = result->Fetch();

			bool perm = fields[1].GetBool();
			uint32 mapId = fields[2].GetUInt16();
			uint32 instanceId = fields[0].GetUInt32();
			uint8 difficulty = fields[3].GetUInt8();

			time_t resetTime = time_t(fields[4].GetUInt32());
			// the resettime for normal instances is only saved when the InstanceSave is unloaded
			// so the value read from the DB may be wrong here but only if the InstanceSave is loaded
			// and in that case it is not used

			MapEntry const* mapEntry = sMapStore.LookupEntry(mapId);
			if (!mapEntry || !mapEntry->IsDungeon()) {
				sLog->outError(
						"_LoadBoundInstances: player %s(%d) has bind to not existed or not dungeon map %d",
						GetName(), GetGUIDLow(), mapId);
				CharacterDatabase.PExecute(
						"DELETE FROM character_instance WHERE guid = '%d' AND instance = '%d'",
						GetGUIDLow(), instanceId);
				continue;
			}

			if (difficulty >= MAX_DIFFICULTY) {
				sLog->outError(
						"_LoadBoundInstances: player %s(%d) has bind to not existed difficulty %d instance for map %u",
						GetName(), GetGUIDLow(), difficulty, mapId);
				CharacterDatabase.PExecute(
						"DELETE FROM character_instance WHERE guid = '%d' AND instance = '%d'",
						GetGUIDLow(), instanceId);
				continue;
			}

			MapDifficulty const* mapDiff = GetMapDifficultyData(mapId,
					Difficulty(difficulty));
			if (!mapDiff) {
				sLog->outError(
						"_LoadBoundInstances: player %s(%d) has bind to not existed difficulty %d instance for map %u",
						GetName(), GetGUIDLow(), difficulty, mapId);
				CharacterDatabase.PExecute(
						"DELETE FROM character_instance WHERE guid = '%d' AND instance = '%d'",
						GetGUIDLow(), instanceId);
				continue;
			}

			if (!perm && group) {
				sLog->outError(
						"_LoadBoundInstances: player %s(%d) is in group %d but has a non-permanent character bind to map %d, %d, %d",
						GetName(), GetGUIDLow(), GUID_LOPART(group->GetGUID()),
						mapId, instanceId, difficulty);
				CharacterDatabase.PExecute(
						"DELETE FROM character_instance WHERE guid = '%d' AND instance = '%d'",
						GetGUIDLow(), instanceId);
				continue;
			}

			// since non permanent binds are always solo bind, they can always be reset
			if (InstanceSave * save = sInstanceSaveMgr->AddInstanceSave(mapId,
					instanceId, Difficulty(difficulty), resetTime, !perm, true))
				BindToInstance(save, perm, true);
		} while (result->NextRow());
	}
}

InstancePlayerBind* Player::GetBoundInstance(uint32 mapid,
		Difficulty difficulty) {
	// some instances only have one difficulty
	MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(mapid,
			difficulty);
	if (!mapDiff)
		return NULL;

	BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
	if (itr != m_boundInstances[difficulty].end())
		return &itr->second;
	else
		return NULL;
}

InstanceSave * Player::GetInstanceSave(uint32 mapid, bool raid) {
	InstancePlayerBind *pBind = GetBoundInstance(mapid, GetDifficulty(raid));
	InstanceSave *pSave = pBind ? pBind->save : NULL;
	if (!pBind || !pBind->perm)
		if (Group *group = GetGroup())
			if (InstanceGroupBind *groupBind = group->GetBoundInstance(this))
				pSave = groupBind->save;

	return pSave;
}

void Player::UnbindInstance(uint32 mapid, Difficulty difficulty, bool unload) {
	BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
	UnbindInstance(itr, difficulty, unload);
}

void Player::UnbindInstance(BoundInstancesMap::iterator &itr,
		Difficulty difficulty, bool unload) {
	if (itr != m_boundInstances[difficulty].end()) {
		if (!unload)
			CharacterDatabase.PExecute(
					"DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'",
					GetGUIDLow(), itr->second.save->GetInstanceId());
		itr->second.save->RemovePlayer(this); // save can become invalid
		m_boundInstances[difficulty].erase(itr++);
	}
}

InstancePlayerBind* Player::BindToInstance(InstanceSave *save, bool permanent,
		bool load) {
	if (save) {
		InstancePlayerBind& bind =
				m_boundInstances[save->GetDifficulty()][save->GetMapId()];
		if (bind.save) {
			// update the save when the group kills a boss
			if (permanent != bind.perm || save != bind.save)
				if (!load)
					CharacterDatabase.PExecute(
							"UPDATE character_instance SET instance = '%u', permanent = '%u' WHERE guid = '%u' AND instance = '%u'",
							save->GetInstanceId(), permanent, GetGUIDLow(),
							bind.save->GetInstanceId());
		} else if (!load)
			CharacterDatabase.PExecute(
					"INSERT INTO character_instance (guid, instance, permanent) VALUES ('%u', '%u', '%u')",
					GetGUIDLow(), save->GetInstanceId(), permanent);

		if (bind.save != save) {
			if (bind.save)
				bind.save->RemovePlayer(this);
			save->AddPlayer(this);
		}

		if (permanent)
			save->SetCanReset(false);

		bind.save = save;
		bind.perm = permanent;
		if (!load)
			sLog->outDebug(
					LOG_FILTER_PLAYER_LOADING,
					"Player::BindToInstance: %s(%d) is now bound to map %d, instance %d, difficulty %d",
					GetName(), GetGUIDLow(), save->GetMapId(),
					save->GetInstanceId(), save->GetDifficulty());
		sScriptMgr->OnPlayerBindToInstance(this, save->GetDifficulty(),
				save->GetMapId(), permanent);
		return &bind;
	} else
		return NULL;
}

void Player::BindToInstance() {
	// Player left the instance
	if (_pendingBind->GetInstanceId() != GetInstanceId())
		return;

	WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
	data << uint32(0);
	GetSession()->SendPacket(&data);
	BindToInstance(_pendingBind, true);
}

void Player::SendRaidInfo() {
	uint32 counter = 0;

	WorldPacket data(SMSG_RAID_INSTANCE_INFO, 4);

	size_t p_counter = data.wpos();
	data << uint32(counter); // placeholder

	time_t now = time(NULL);

	for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) {
		for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin();
				itr != m_boundInstances[i].end(); ++itr) {
			if (itr->second.perm) {
				InstanceSave *save = itr->second.save;
				data << uint32(save->GetMapId()); // map id
				data << uint32(save->GetDifficulty()); // difficulty
				data << uint64(save->GetInstanceId()); // instance id
				data << uint8(1); // expired = 0
				data << uint8(0); // extended = 1
				data << uint32(save->GetResetTime() - now); // reset time
				++counter;
			}
		}
	}
	data.put<uint32>(p_counter, counter);
	GetSession()->SendPacket(&data);
}

/*
 - called on every successful teleportation to a map
 */
void Player::SendSavedInstances() {
	bool hasBeenSaved = false;
	WorldPacket data;

	for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) {
		for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin();
				itr != m_boundInstances[i].end(); ++itr) {
			if (itr->second.perm) // only permanent binds are sent
			{
				hasBeenSaved = true;
				break;
			}
		}
	}

	//Send opcode 811. true or false means, whether you have current raid/heroic instances
	data.Initialize(SMSG_UPDATE_INSTANCE_OWNERSHIP);
	data << uint32(hasBeenSaved);
	GetSession()->SendPacket(&data);

	if (!hasBeenSaved)
		return;

	for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) {
		for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin();
				itr != m_boundInstances[i].end(); ++itr) {
			if (itr->second.perm) {
				data.Initialize(SMSG_UPDATE_LAST_INSTANCE);
				data << uint32(itr->second.save->GetMapId());
				GetSession()->SendPacket(&data);
			}
		}
	}
}

/// convert the player's binds to the group
void Player::ConvertInstancesToGroup(Player *player, Group *group,
		uint64 player_guid) {
	bool has_binds = false;
	bool has_solo = false;

	if (player) {
		player_guid = player->GetGUID();
		if (!group)
			group = player->GetGroup();
	}
	ASSERT(player_guid);

	// copy all binds to the group, when changing leader it's assumed the character
	// will not have any solo binds

	if (player) {
		for (uint8 i = 0; i < MAX_DIFFICULTY; ++i) {
			for (BoundInstancesMap::iterator itr =
					player->m_boundInstances[i].begin();
					itr != player->m_boundInstances[i].end();) {
				has_binds = true;
				if (group)
					group->BindToInstance(itr->second.save, itr->second.perm,
							true);
				// permanent binds are not removed
				if (!itr->second.perm) {
					// increments itr in call
					player->UnbindInstance(itr, Difficulty(i), true);
					has_solo = true;
				} else
					++itr;
			}
		}
	}

	// if the player's not online we don't know what binds it has
	if (!player || !group || has_binds)
		CharacterDatabase.PExecute(
				"INSERT INTO group_instance SELECT guid, instance, permanent FROM character_instance WHERE guid = '%u'",
				GUID_LOPART(player_guid));
	// the following should not get executed when changing leaders
	if (!player || has_solo)
		CharacterDatabase.PExecute(
				"DELETE FROM character_instance WHERE guid = '%d' AND permanent = 0",
				GUID_LOPART(player_guid));
}

bool Player::Satisfy(AccessRequirement const* ar, uint32 target_map,
		bool report) {
	if (!isGameMaster() && ar) {
		uint8 LevelMin = 0;
		uint8 LevelMax = 0;

		MapEntry const* mapEntry = sMapStore.LookupEntry(target_map);
		if (!mapEntry)
			return false;

		if (!sWorld->getBoolConfig(CONFIG_INSTANCE_IGNORE_LEVEL)) {
			if (ar->levelMin && getLevel() < ar->levelMin)
				LevelMin = ar->levelMin;
			if (ar->levelMax && getLevel() > ar->levelMax)
				LevelMax = ar->levelMax;
		}

		uint32 missingItem = 0;
		if (ar->item) {
			if (!HasItemCount(ar->item, 1)
					&& (!ar->item2 || !HasItemCount(ar->item2, 1)))
				missingItem = ar->item;
		} else if (ar->item2 && !HasItemCount(ar->item2, 1))
			missingItem = ar->item2;

		if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_MAP, target_map, this)) {
			GetSession()->SendAreaTriggerMessage("%s",
					GetSession()->GetArkCoreString(LANG_INSTANCE_CLOSED));
			return false;
		}

		uint32 missingQuest = 0;
		if (GetTeam() == ALLIANCE && ar->quest_A
				&& !GetQuestRewardStatus(ar->quest_A))
			missingQuest = ar->quest_A;
		else if (GetTeam() == HORDE && ar->quest_H
				&& !GetQuestRewardStatus(ar->quest_H))
			missingQuest = ar->quest_H;

		uint32 missingAchievement = 0;
		if (ar->achievement
				&& !GetAchievementMgr().HasAchieved(
						sAchievementStore.LookupEntry(ar->achievement)))
			missingAchievement = ar->achievement;

		Difficulty target_difficulty = GetDifficulty(mapEntry->IsRaid());
		MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(
				target_map, target_difficulty);
		if (LevelMin || LevelMax || missingItem || missingQuest
				|| missingAchievement) {
			if (report) {
				if (missingQuest && !ar->questFailedText.empty())
					ChatHandler(GetSession()).PSendSysMessage("%s",
							ar->questFailedText.c_str());
				else if (mapDiff->hasErrorMessage) // if (missingAchievement) covered by this case
					SendTransferAborted(target_map, TRANSFER_ABORT_DIFFICULTY,
							target_difficulty);
				else if (missingItem)
					GetSession()->SendAreaTriggerMessage(
							GetSession()->GetArkCoreString(
									LANG_LEVEL_MINREQUIRED_AND_ITEM), LevelMin,
							ObjectMgr::GetItemPrototype(missingItem)->Name1);
				else if (LevelMin)
					GetSession()->SendAreaTriggerMessage(
							GetSession()->GetArkCoreString(
									LANG_LEVEL_MINREQUIRED), LevelMin);
			}
			return false;
		}
	}
	return true;
}

bool Player::CheckInstanceLoginValid() {
	if (!GetMap())
		return false;

	if (!GetMap()->IsDungeon() || isGameMaster())
		return true;

	if (GetMap()->IsRaid()) {
		// cannot be in raid instance without a group
		if (!GetGroup())
			return false;
	} else {
		// cannot be in normal instance without a group and more players than 1 in instance
		if (!GetGroup() && GetMap()->GetPlayersCountExceptGMs() > 1)
			return false;
	}

	// do checks for satisfy accessreqs, instance full, encounter in progress (raid), perm bind group != perm bind player
	return sMapMgr->CanPlayerEnter(GetMap()->GetId(), this, true);
}

bool Player::_LoadHomeBind(PreparedQueryResult result) {
	PlayerInfo const *info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
	if (!info) {
		sLog->outError(
				"Player have incorrect race/class pair. Can't be loaded.");
		return false;
	}

	bool ok = false;
	//QueryResult *result = CharacterDatabase.PQuery("SELECT map, zone, position_x, position_y, position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(playerGuid));
	if (result) {
		Field* fields = result->Fetch();

		m_homebindMapId = fields[0].GetUInt16();
		m_homebindAreaId = fields[1].GetUInt16();
		m_homebindX = fields[2].GetFloat();
		m_homebindY = fields[3].GetFloat();
		m_homebindZ = fields[4].GetFloat();

		MapEntry const* bindMapEntry = sMapStore.LookupEntry(m_homebindMapId);

		// accept saved data only for valid position (and non instanceable), and accessable
		if (MapManager::IsValidMapCoord(m_homebindMapId, m_homebindX,
				m_homebindY, m_homebindZ) && !bindMapEntry->Instanceable()
				&& GetSession()->Expansion() >= bindMapEntry->Expansion())
			ok = true;
		else
			CharacterDatabase.PExecute(
					"DELETE FROM character_homebind WHERE guid = '%u'",
					GetGUIDLow());
	}

	if (!ok) {
		m_homebindMapId = info->mapId;
		m_homebindAreaId = info->areaId;
		m_homebindX = info->positionX;
		m_homebindY = info->positionY;
		m_homebindZ = info->positionZ;

		CharacterDatabase.PExecute(
				"INSERT INTO character_homebind (guid, map, zone, position_x, position_y, position_z) VALUES ('%u', '%u', '%u', '%f', '%f', '%f')",
				GetGUIDLow(), m_homebindMapId, m_homebindAreaId, m_homebindX,
				m_homebindY, m_homebindZ);
	}

	sLog->outStaticDebug(
			"Setting player home position - mapid: %u, areaid: %u, X: %f, Y: %f, Z: %f",
			m_homebindMapId, m_homebindAreaId, m_homebindX, m_homebindY,
			m_homebindZ);

	return true;
}

/*********************************************************/
/***                   SAVE SYSTEM                     ***/
/*********************************************************/

void Player::SaveToDB() {
	// delay auto save at any saves (manual, in code, or autosave)
	m_nextSave = sWorld->getIntConfig(CONFIG_INTERVAL_SAVE);

	//lets allow only players in world to be saved
	if (IsBeingTeleportedFar()) {
		ScheduleDelayedOperation(DELAYED_SAVE_PLAYER);
		return;
	}

	// first save/honor gain after midnight will also update the player's honor fields
	UpdateHonorFields();

	sLog->outDebug(LOG_FILTER_UNITS, "The value of player %s at save: ",
			m_name.c_str());
	outDebugValues();

	std::string sql_name = m_name;
	CharacterDatabase.EscapeString(sql_name);

	//TODO: drop ammoid
	//      drop stable_slots
	std::ostringstream ss;
	ss
			<< "REPLACE INTO characters (guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, "
					"map, instance_id, instance_mode_mask, position_x, position_y, position_z, orientation, "
					"taximask, online, cinematic, "
					"totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, "
					"trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, "
					"death_expire_time, taxi_path, totalKills, "
					"todayKills, yesterdayKills, chosenTitle, watchedFaction, drunk, health, power1, power2, power3, "
					"power4, power5, power6, power7, power8, power9, power10, latency, speccount, activespec, exploredZones, equipmentCache, ammoId, "
					"knownTitles, actionBars, currentPetSlot, petSlotUsed) VALUES ("
			<< GetGUIDLow() << ", " << GetSession()->GetAccountId() << ", '"
			<< sql_name << "', " << uint32(getRace()) << ", "
			<< uint32(getClass()) << ", " << uint32(getGender()) << ", "
			<< uint32(getLevel()) << ", " << GetUInt32Value(PLAYER_XP) << ", "
			<< GetMoney() << ", " << GetUInt32Value(PLAYER_BYTES) << ", "
			<< GetUInt32Value(PLAYER_BYTES_2) << ", "
			<< GetUInt32Value(PLAYER_FLAGS) << ", ";

	if (!IsBeingTeleported()) {
		ss << GetMapId() << ", " << (uint32) GetInstanceId() << ", "
				<< uint32(
						uint8(GetDungeonDifficulty())
								| uint8(GetRaidDifficulty()) << 4) << ", "
				<< finiteAlways(GetPositionX()) << ", "
				<< finiteAlways(GetPositionY()) << ", "
				<< finiteAlways(GetPositionZ()) << ", "
				<< finiteAlways(GetOrientation()) << ", ";
	} else {
		ss << GetTeleportDest().GetMapId() << ", " << (uint32) 0 << ", "
				<< uint32(
						uint8(GetDungeonDifficulty())
								| uint8(GetRaidDifficulty()) << 4) << ", "
				<< finiteAlways(GetTeleportDest().GetPositionX()) << ", "
				<< finiteAlways(GetTeleportDest().GetPositionY()) << ", "
				<< finiteAlways(GetTeleportDest().GetPositionZ()) << ", "
				<< finiteAlways(GetTeleportDest().GetOrientation()) << ", ";
	}

	ss << m_taxi << ", "; // string with TaxiMaskSize numbers

	ss << (IsInWorld() ? 1 : 0) << ", ";

	ss << uint32(m_cinematic) << ", ";

	ss << m_Played_time[PLAYED_TIME_TOTAL] << ", ";
	ss << m_Played_time[PLAYED_TIME_LEVEL] << ", ";

	ss << finiteAlways(m_rest_bonus) << ", ";
	ss << uint32(time(NULL)) << ", ";
	ss << (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0) << ", ";
	//save, far from tavern/city
	//save, but in tavern/city
	ss << m_resetTalentsCost << ", ";
	ss << uint32(m_resetTalentsTime) << ", ";

	ss << finiteAlways(m_movementInfo.t_pos.GetPositionX()) << ", ";
	ss << finiteAlways(m_movementInfo.t_pos.GetPositionY()) << ", ";
	ss << finiteAlways(m_movementInfo.t_pos.GetPositionZ()) << ", ";
	ss << finiteAlways(m_movementInfo.t_pos.GetOrientation()) << ", ";
	if (m_transport)
		ss << m_transport->GetGUIDLow();
	else
		ss << "0";
	ss << ", ";

	ss << m_ExtraFlags << ", ";

	ss << uint32(0) << ", ";

	ss << uint32(m_atLoginFlags) << ", ";

	ss << GetZoneId() << ", ";

	ss << uint32(m_deathExpireTime) << ", '";

	ss << m_taxi.SaveTaxiDestinationsToString() << "', ";

	ss << uint32(0) /*GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS)*/
	<< ", ";

	ss << GetUInt16Value(PLAYER_FIELD_KILLS, 0) << ", ";

	ss << GetUInt16Value(PLAYER_FIELD_KILLS, 1) << ", ";

	ss << GetUInt32Value(PLAYER_CHOSEN_TITLE) << ", ";

	ss << GetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX) << ", ";

	ss << (uint16) (GetUInt32Value(PLAYER_BYTES_3) & 0xFFFE) << ", ";

	ss << GetHealth();

	for (uint32 i = 0; i < MAX_POWERS; ++i)
		ss << ", " << GetPower(Powers(i));
	ss << ", ";
	ss << GetSession()->GetLatency();
	ss << ", ";
	ss << uint32(m_specsCount);
	ss << ", ";
	ss << uint32(m_activeSpec) << ", '";
	for (uint32 i = 0; i < PLAYER_EXPLORED_ZONES_SIZE; ++i) {
		ss << GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + i) << " ";
	}

	ss << "', '";
	for (uint32 i = 0; i < EQUIPMENT_SLOT_END * 2; ++i) {
		ss << GetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + i) << " ";
	}

	ss << "', ";

	ss << uint32(0) /*GetUInt32Value(PLAYER_AMMO_ID)*/<< ", '";
	for (uint32 i = 0; i < KNOWN_TITLES_SIZE * 2; ++i) {
		ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << " ";
	}
	ss << "', ";
	ss << uint32(GetByteValue(PLAYER_FIELD_BYTES, 2));

	ss << ", " << m_currentPetSlot << ", " << m_petSlotUsed;

	ss << ")";

	SQLTransaction trans = CharacterDatabase.BeginTransaction();

	trans->Append(ss.str().c_str());

	if (m_mailsUpdated) //save mails only when needed
		_SaveMail(trans);

	_SaveBGData(trans);
	_SaveInventory(trans);
	_SaveQuestStatus(trans);
	_SaveDailyQuestStatus(trans);
	_SaveWeeklyQuestStatus(trans);
	_SaveTalents(trans);
	_SaveTalentBranchSpecs(trans);
	_SaveSpells(trans);
	_SaveSpellCooldowns(trans);
	_SaveActions(trans);
	_SaveAuras(trans);
	_SaveSkills(trans);
	m_achievementMgr.SaveToDB(trans);
	m_reputationMgr.SaveToDB(trans);
	_SaveEquipmentSets(trans);
	GetSession()->SaveTutorialsData(trans); // changed only while character in game
	_SaveGlyphs(trans);
	_SaveInstanceTimeRestrictions(trans);
	_SaveCurrency();
	_SaveConquestPointsWeekCap();

	// check if stats should only be saved on logout
	// save stats can be out of transaction
	if (m_session->isLogingOut()
			|| !sWorld->getBoolConfig(CONFIG_STATS_SAVE_ONLY_ON_LOGOUT))
		_SaveStats(trans);

	CharacterDatabase.CommitTransaction(trans);

	// save pet (hunter pet level and experience and all type pets health/mana).
	if (Pet* pet = GetPet())
		pet->SavePetToDB(PET_SLOT_ACTUAL_PET_SLOT);
}

// fast save function for item/money cheating preventing - save only inventory and money state
void Player::SaveInventoryAndGoldToDB(SQLTransaction& trans) {
	_SaveInventory(trans);
	SaveGoldToDB(trans);
}

void Player::SaveGoldToDB(SQLTransaction& trans) {
	trans->PAppend("UPDATE characters SET money = '%u' WHERE guid = '%u'",
			GetMoney(), GetGUIDLow());
}

void Player::_SaveActions(SQLTransaction& trans) {
	for (ActionButtonList::iterator itr = m_actionButtons.begin();
			itr != m_actionButtons.end();) {
		switch (itr->second.uState) {
		case ACTIONBUTTON_NEW:
			trans->PAppend(
					"INSERT INTO character_action (guid, spec, button, action, type) VALUES ('%u', '%u', '%u', '%u', '%u')",
					GetGUIDLow(), m_activeSpec, (uint32) itr->first,
					(uint32) itr->second.GetAction(),
					(uint32) itr->second.GetType());
			itr->second.uState = ACTIONBUTTON_UNCHANGED;
			++itr;
			break;
		case ACTIONBUTTON_CHANGED:
			trans->PAppend(
					"UPDATE character_action SET action = '%u', type = '%u' WHERE guid = '%u' AND button = '%u' AND spec = '%u'",
					(uint32) itr->second.GetAction(),
					(uint32) itr->second.GetType(), GetGUIDLow(),
					(uint32) itr->first, m_activeSpec);
			itr->second.uState = ACTIONBUTTON_UNCHANGED;
			++itr;
			break;
		case ACTIONBUTTON_DELETED:
			trans->PAppend(
					"DELETE FROM character_action WHERE guid = '%u' and button = '%u' and spec = '%u'",
					GetGUIDLow(), (uint32) itr->first, m_activeSpec);
			m_actionButtons.erase(itr++);
			break;
		default:
			++itr;
			break;
		}
	}
}

void Player::_SaveAuras(SQLTransaction& trans) {
	PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(
			CHAR_DEL_AURA);
	stmt->setUInt32(0, GetGUIDLow());
	trans->Append(stmt);

	for (AuraMap::const_iterator itr = m_ownedAuras.begin();
			itr != m_ownedAuras.end(); ++itr) {
		if (!itr->second->CanBeSaved())
			continue;

		Aura * aura = itr->second;

		int32 damage[MAX_SPELL_EFFECTS];
		int32 baseDamage[MAX_SPELL_EFFECTS];
		uint8 effMask = 0;
		uint8 recalculateMask = 0;
		for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) {
			if (AuraEffect const* effect = aura->GetEffect(i)) {
				baseDamage[i] = effect->GetBaseAmount();
				damage[i] = effect->GetAmount();
				effMask |= 1 << i;
				if (effect->CanBeRecalculated())
					recalculateMask |= 1 << i;
			} else {
				baseDamage[i] = 0;
				damage[i] = 0;
			}
		}

		uint8 index = 0;
		stmt = CharacterDatabase.GetPreparedStatement(CHAR_ADD_AURA);
		stmt->setUInt32(index++, GetGUIDLow());
		stmt->setUInt64(index++, itr->second->GetCasterGUID());
		stmt->setUInt64(index++, itr->second->GetCastItemGUID());
		stmt->setUInt32(index++, itr->second->GetId());
		stmt->setUInt8(index++, effMask);
		stmt->setUInt8(index++, recalculateMask);
		stmt->setUInt8(index++, itr->second->GetStackAmount());
		stmt->setInt32(index++, damage[0]);
		stmt->setInt32(index++, damage[1]);
		stmt->setInt32(index++, damage[2]);
		stmt->setInt32(index++, baseDamage[0]);
		stmt->setInt32(index++, baseDamage[1]);
		stmt->setInt32(index++, baseDamage[2]);
		stmt->setInt32(index++, itr->second->GetMaxDuration());
		stmt->setInt32(index++, itr->second->GetDuration());
		stmt->setUInt8(index, itr->second->GetCharges());
		trans->Append(stmt);
	}
}

void Player::_SaveInventory(SQLTransaction& trans) {
	// force items in buyback slots to new state
	// and remove those that aren't already
	for (uint8 i = BUYBACK_SLOT_START; i < BUYBACK_SLOT_END; ++i) {
		Item *item = m_items[i];
		if (!item || item->GetState() == ITEM_NEW)
			continue;
		trans->PAppend("DELETE FROM character_inventory WHERE item = '%u'",
				item->GetGUIDLow());
		PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(
				CHAR_DEL_ITEM_INSTANCE);
		stmt->setUInt32(0, item->GetGUIDLow());
		trans->Append(stmt);
		m_items[i]->FSetState(ITEM_NEW);
	}

	// Updated played time for refundable items. We don't do this in Player::Update because there's simply no need for it,
	// the client auto counts down in real time after having received the initial played time on the first
	// SMSG_ITEM_REFUND_INFO_RESPONSE packet.
	// Item::UpdatePlayedTime is only called when needed, which is in DB saves, and item refund info requests.
	std::set<uint32>::iterator i_next;
	for (std::set<uint32>::iterator itr = m_refundableItems.begin();
			itr != m_refundableItems.end(); itr = i_next) {
		// use copy iterator because itr may be invalid after operations in this loop
		i_next = itr;
		++i_next;

		Item* iPtr = GetItemByGuid(MAKE_NEW_GUID(*itr, 0, HIGHGUID_ITEM));
		if (iPtr) {
			iPtr->UpdatePlayedTime(this);
			continue;
		} else {
			sLog->outError(
					"Can't find item guid %u but is in refundable storage for player %u ! Removing.",
					*itr, GetGUIDLow());
			m_refundableItems.erase(itr);
		}
	}

	// update enchantment durations
	for (EnchantDurationList::iterator itr = m_enchantDuration.begin();
			itr != m_enchantDuration.end(); ++itr)
		itr->item->SetEnchantmentDuration(itr->slot, itr->leftduration, this);

	// if no changes
	if (m_itemUpdateQueue.empty())
		return;

	uint32 lowGuid = GetGUIDLow();
	for (size_t i = 0; i < m_itemUpdateQueue.size(); ++i) {
		Item *item = m_itemUpdateQueue[i];
		if (!item)
			continue;

		Bag *container = item->GetContainer();
		uint32 bag_guid = container ? container->GetGUIDLow() : 0;

		if (item->GetState() != ITEM_REMOVED) {
			Item *test = GetItemByPos(item->GetBagSlot(), item->GetSlot());
			if (test == NULL) {
				uint32 bagTestGUID = 0;
				if (Item* test2 = GetItemByPos(INVENTORY_SLOT_BAG_0, item->GetBagSlot()))
					bagTestGUID = test2->GetGUIDLow();
				sLog->outError(
						"Player(GUID: %u Name: %s)::_SaveInventory - the bag(%u) and slot(%u) values for the item with guid %u (state %d) are incorrect, the player doesn't have an item at that position!",
						lowGuid, GetName(), item->GetBagSlot(), item->GetSlot(),
						item->GetGUIDLow(), (int32) item->GetState());
				// according to the test that was just performed nothing should be in this slot, delete
				trans->PAppend(
						"DELETE FROM character_inventory WHERE bag=%u AND slot=%u AND guid=%u",
						bagTestGUID, item->GetSlot(), lowGuid);
				// also THIS item should be somewhere else, cheat attempt
				item->FSetState(ITEM_REMOVED); // we are IN updateQueue right now, can't use SetState which modifies the queue
				DeleteRefundReference(item->GetGUIDLow());
				// don't skip, let the switch delete it
				//continue;
			} else if (test != item) {
				sLog->outError(
						"Player(GUID: %u Name: %s)::_SaveInventory - the bag(%u) and slot(%u) values for the item with guid %u are incorrect, the item with guid %u is there instead!",
						lowGuid, GetName(), item->GetBagSlot(), item->GetSlot(),
						item->GetGUIDLow(), test->GetGUIDLow());
				// save all changes to the item...
				if (item->GetState() != ITEM_NEW) // only for existing items, no dupes
					item->SaveToDB(trans);
				// ...but do not save position in invntory
				continue;
			}
		}

		PreparedStatement* stmt = NULL;
		switch (item->GetState()) {
		case ITEM_NEW:
		case ITEM_CHANGED:
			stmt = CharacterDatabase.GetPreparedStatement(
					CHAR_REP_INVENTORY_ITEM);
			stmt->setUInt32(0, lowGuid);
			stmt->setUInt32(1, bag_guid);
			stmt->setUInt8(2, item->GetSlot());
			stmt->setUInt32(3, item->GetGUIDLow());
			trans->Append(stmt);
			break;
		case ITEM_REMOVED:
			stmt = CharacterDatabase.GetPreparedStatement(
					CHAR_DEL_INVENTORY_ITEM);
			stmt->setUInt32(0, item->GetGUIDLow());
			trans->Append(stmt);
		case ITEM_UNCHANGED:
			break;
		}

		item->SaveToDB(trans); // item have unchanged inventory record and can be save standalone
	}
	m_itemUpdateQueue.clear();
}

void Player::_SaveMail(SQLTransaction& trans) {
	if (!m_mailsLoaded)
		return;

	for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();
			++itr) {
		Mail *m = (*itr);
		if (m->state == MAIL_STATE_CHANGED) {
			trans->PAppend(
					"UPDATE mail SET has_items = '%u', expire_time = '" UI64FMTD "', deliver_time = '" UI64FMTD "', money = '%u', cod = '%u', checked = '%u' WHERE id = '%u'",
					m->HasItems() ? 1 : 0, (uint64) m->expire_time,
					(uint64) m->deliver_time, m->money, m->COD, m->checked,
					m->messageID);
			if (m->removedItems.size()) {
				for (std::vector<uint32>::iterator itr2 =
						m->removedItems.begin(); itr2 != m->removedItems.end();
						++itr2)
					trans->PAppend(
							"DELETE FROM mail_items WHERE item_guid = '%u'",
							*itr2);
				m->removedItems.clear();
			}
			m->state = MAIL_STATE_UNCHANGED;
		} else if (m->state == MAIL_STATE_DELETED) {
			if (m->HasItems()) {
				PreparedStatement* stmt = NULL;
				for (MailItemInfoVec::iterator itr2 = m->items.begin();
						itr2 != m->items.end(); ++itr2) {
					stmt = CharacterDatabase.GetPreparedStatement(
							CHAR_DEL_ITEM_INSTANCE);
					stmt->setUInt32(0, itr2->item_guid);
					trans->Append(stmt);
				}
			}
			trans->PAppend("DELETE FROM mail WHERE id = '%u'", m->messageID);
			trans->PAppend("DELETE FROM mail_items WHERE mail_id = '%u'",
					m->messageID);
		}
	}

	//deallocate deleted mails...
	for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();) {
		if ((*itr)->state == MAIL_STATE_DELETED) {
			Mail* m = *itr;
			m_mail.erase(itr);
			delete m;
			itr = m_mail.begin();
		} else
			++itr;
	}

	m_mailsUpdated = false;
}

void Player::_SaveQuestStatus(SQLTransaction& trans) {
	QuestStatusSaveMap::iterator saveItr;
	QuestStatusMap::iterator statusItr;

	bool keepAbandoned = !(sWorld->GetCleaningFlags()
			& CharacterDatabaseCleaner::CLEANING_FLAG_QUESTSTATUS);

	for (saveItr = m_QuestStatusSave.begin();
			saveItr != m_QuestStatusSave.end(); ++saveItr) {
		if (saveItr->second) {
			statusItr = m_QuestStatus.find(saveItr->first);
			if (statusItr != m_QuestStatus.end()
					&& (keepAbandoned
							|| statusItr->second.m_status != QUEST_STATUS_NONE)) {
				trans->PAppend(
						"REPLACE INTO character_queststatus (guid, quest, status, explored, timer, mobcount1, mobcount2, mobcount3, mobcount4, itemcount1, itemcount2, itemcount3, itemcount4) "
								"VALUES ('%u', '%u', '%u', '%u', '" UI64FMTD "', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')",
						GetGUIDLow(),
						statusItr->first,
						statusItr->second.m_status,
						statusItr->second.m_explored,
						uint64(
								statusItr->second.m_timer / IN_MILLISECONDS
										+ sWorld->GetGameTime()),
						statusItr->second.m_creatureOrGOcount[0],
						statusItr->second.m_creatureOrGOcount[1],
						statusItr->second.m_creatureOrGOcount[2],
						statusItr->second.m_creatureOrGOcount[3],
						statusItr->second.m_itemcount[0],
						statusItr->second.m_itemcount[1],
						statusItr->second.m_itemcount[2],
						statusItr->second.m_itemcount[3]);
			}
		} else
			trans->PAppend(
					"DELETE FROM character_queststatus WHERE guid = %u AND quest = %u",
					GetGUIDLow(), saveItr->first);
	}

	m_QuestStatusSave.clear();

	for (saveItr = m_RewardedQuestsSave.begin();
			saveItr != m_RewardedQuestsSave.end(); ++saveItr) {
		if (saveItr->second)
			trans->PAppend(
					"INSERT IGNORE INTO character_queststatus_rewarded (guid, quest) VALUES (%u, %u)",
					GetGUIDLow(), saveItr->first);
		else if (!keepAbandoned)
			trans->PAppend(
					"DELETE FROM character_queststatus_rewarded WHERE guid = %u AND quest = %u",
					GetGUIDLow(), saveItr->first);
	}

	m_RewardedQuestsSave.clear();
}

void Player::_SaveDailyQuestStatus(SQLTransaction& trans) {
	if (!m_DailyQuestChanged)
		return;

	m_DailyQuestChanged = false;

	// save last daily quest time for all quests: we need only mostly reset time for reset check anyway

	// we don't need transactions here.
	trans->PAppend("DELETE FROM character_queststatus_daily WHERE guid = '%u'",
			GetGUIDLow());
	for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS;
			++quest_daily_idx)
		if (GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx))
			trans->PAppend(
					"INSERT INTO character_queststatus_daily (guid, quest, time) VALUES ('%u', '%u', '" UI64FMTD "')",
					GetGUIDLow(),
					GetUInt32Value(
							PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx),
					uint64(m_lastDailyQuestTime));

	if (!m_DFQuests.empty()) {
		for (DFQuestsDoneList::iterator itr = m_DFQuests.begin();
				itr != m_DFQuests.end(); ++itr) {
			trans->PAppend(
					"INSERT INTO character_queststatus_daily (guid, quest, time) VALUES ('%u', '%u', '" UI64FMTD "')",
					GetGUIDLow(), (*itr), uint64(m_lastDailyQuestTime));
		}
	}
}

void Player::_SaveWeeklyQuestStatus(SQLTransaction& trans) {
	if (!m_WeeklyQuestChanged || m_weeklyquests.empty())
		return;

	// we don't need transactions here.
	trans->PAppend("DELETE FROM character_queststatus_weekly WHERE guid = '%u'",
			GetGUIDLow());

	for (QuestSet::const_iterator iter = m_weeklyquests.begin();
			iter != m_weeklyquests.end(); ++iter) {
		uint32 quest_id = *iter;

		trans->PAppend(
				"INSERT INTO character_queststatus_weekly (guid, quest) VALUES ('%u', '%u')",
				GetGUIDLow(), quest_id);
	}

	m_WeeklyQuestChanged = false;
}

void Player::_SaveSkills(SQLTransaction& trans) {
	// we don't need transactions here.
	for (SkillStatusMap::iterator itr = mSkillStatus.begin();
			itr != mSkillStatus.end();) {
		if (itr->second.uState == SKILL_UNCHANGED) {
			++itr;
			continue;
		}

		if (itr->second.uState == SKILL_DELETED) {
			trans->PAppend(
					"DELETE FROM character_skills WHERE guid = '%u' AND skill = '%u' ",
					GetGUIDLow(), itr->first);
			mSkillStatus.erase(itr++);
			continue;
		}

		uint32 valueData = GetUInt32Value(
				PLAYER_SKILL_VALUE_INDEX(itr->second.pos));
		uint16 value = SKILL_VALUE(valueData);
		uint16 max = SKILL_MAX(valueData);

		switch (itr->second.uState) {
		case SKILL_NEW:
			trans->PAppend(
					"INSERT INTO character_skills (guid, skill, value, max) VALUES ('%u', '%u', '%u', '%u')",
					GetGUIDLow(), itr->first, value, max);
			break;
		case SKILL_CHANGED:
			trans->PAppend(
					"UPDATE character_skills SET value = '%u', max = '%u'WHERE guid = '%u' AND skill = '%u' ",
					value, max, GetGUIDLow(), itr->first);
			break;
		default:
			break;
		}
		itr->second.uState = SKILL_UNCHANGED;

		++itr;
	}
}

void Player::_SaveSpells(SQLTransaction& trans) {
	for (PlayerSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end();
			) {
		if (itr->second->state == PLAYERSPELL_REMOVED
				|| itr->second->state == PLAYERSPELL_CHANGED)
			trans->PAppend(
					"DELETE FROM character_spell WHERE guid = '%u' and spell = '%u'",
					GetGUIDLow(), itr->first);

		// add only changed/new not dependent spells
		if (!itr->second->dependent
				&& (itr->second->state == PLAYERSPELL_NEW
						|| itr->second->state == PLAYERSPELL_CHANGED))
			trans->PAppend(
					"INSERT INTO character_spell (guid, spell, active, disabled) VALUES ('%u', '%u', '%u', '%u')",
					GetGUIDLow(), itr->first, itr->second->active ? 1 : 0,
					itr->second->disabled ? 1 : 0);

		if (itr->second->state == PLAYERSPELL_REMOVED) {
			delete itr->second;
			m_spells.erase(itr++);
		} else {
			itr->second->state = PLAYERSPELL_UNCHANGED;
			++itr;
		}
	}
}

void Player::_SaveCurrency() {
	for (PlayerCurrenciesMap::iterator itr = m_currencies.begin();
			itr != m_currencies.end();) {
		if (itr->second.state == PLAYERCURRENCY_CHANGED)
			CharacterDatabase.PExecute(
					"UPDATE character_currency SET `count` = '%u', thisweek = '%u' WHERE guid = '%u' AND currency = '%u'",
					itr->second.totalCount, itr->second.weekCount, GetGUIDLow(),
					itr->first);
		else if (itr->second.state == PLAYERCURRENCY_NEW)
			CharacterDatabase.PExecute(
					"INSERT INTO character_currency (guid, currency, `count`, thisweek) VALUES ('%u', '%u', '%u', '%u')",
					GetGUIDLow(), itr->first, itr->second.totalCount,
					itr->second.weekCount);

		if (itr->second.state == PLAYERCURRENCY_REMOVED)
			m_currencies.erase(itr++);
		else {
			itr->second.state = PLAYERCURRENCY_UNCHANGED;
			++itr;
		}
	}
}

void Player::_SaveConquestPointsWeekCap() {
	CharacterDatabase.PExecute(
			"DELETE FROM character_cp_weekcap WHERE guid = '%u'", GetGUIDLow());
	for (uint8 source = 0; source < CP_SOURCE_MAX; source++) {
		CharacterDatabase.PExecute(
				"INSERT INTO character_cp_weekcap (guid, source, maxWeekRating, weekCap) VALUES ('%u', '%u', '%u', '%u')",
				GetGUIDLow(), source, m_maxWeekRating[source],
				m_conquestPointsWeekCap[source]);
	}
}

// save player stats -- only for external usage
// real stats will be recalculated on player login
void Player::_SaveStats(SQLTransaction& trans) {
	// check if stat saving is enabled and if char level is high enough
	if (!sWorld->getIntConfig(CONFIG_MIN_LEVEL_STAT_SAVE)
			|| getLevel() < sWorld->getIntConfig(CONFIG_MIN_LEVEL_STAT_SAVE))
		return;

	trans->PAppend("DELETE FROM character_stats WHERE guid = '%u'",
			GetGUIDLow());
	std::ostringstream ss;
	ss
			<< "INSERT INTO character_stats (guid, maxhealth, maxpower1, maxpower2, maxpower3, maxpower4, maxpower5, maxpower6, maxpower7, maxpower8, maxpower9, maxpower10, "
					"strength, agility, stamina, intellect, spirit, armor, resHoly, resFire, resNature, resFrost, resShadow, resArcane, "
					"blockPct, dodgePct, parryPct, critPct, rangedCritPct, spellCritPct, attackPower, rangedAttackPower, spellPower) VALUES ("
			<< GetGUIDLow() << ", " << GetMaxHealth() << ", ";
	for (uint8 i = 0; i < MAX_POWERS; ++i)
		ss << GetMaxPower(Powers(i)) << ", ";
	for (uint8 i = 0; i < MAX_STATS; ++i)
		ss << GetStat(Stats(i)) << ", ";
	// armor + school resistances
	for (int i = 0; i < MAX_SPELL_SCHOOL; ++i)
		ss << GetResistance(SpellSchools(i)) << ", ";
	ss << GetFloatValue(PLAYER_BLOCK_PERCENTAGE) << ", "
			<< GetFloatValue(PLAYER_DODGE_PERCENTAGE) << ", "
			<< GetFloatValue(PLAYER_PARRY_PERCENTAGE) << ", "
			<< GetFloatValue(PLAYER_CRIT_PERCENTAGE) << ", "
			<< GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE) << ", "
			<< GetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1) << ", "
			<< GetUInt32Value(UNIT_FIELD_ATTACK_POWER) << ", "
			<< GetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER) << ", "
			<< GetBaseSpellPowerBonus() << ")";
	trans->Append(ss.str().c_str());
}

void Player::outDebugValues() const {
	if (!sLog->IsOutDebug()) // optimize disabled debug output
		return;

	sLog->outDebug(LOG_FILTER_UNITS, "HP is: \t\t\t%u\t\tMP is: \t\t\t%u",
			GetMaxHealth(), GetMaxPower(POWER_MANA));
	sLog->outDebug(LOG_FILTER_UNITS,
			"AGILITY is: \t\t%f\t\tSTRENGTH is: \t\t%f", GetStat(STAT_AGILITY),
			GetStat(STAT_STRENGTH));
	sLog->outDebug(LOG_FILTER_UNITS,
			"INTELLECT is: \t\t%f\t\tSPIRIT is: \t\t%f",
			GetStat(STAT_INTELLECT), GetStat(STAT_SPIRIT));
	sLog->outDebug(LOG_FILTER_UNITS, "STAMINA is: \t\t%f",
			GetStat(STAT_STAMINA));
	sLog->outDebug(LOG_FILTER_UNITS, "Armor is: \t\t%u\t\tBlock is: \t\t%f",
			GetArmor(), GetFloatValue(PLAYER_BLOCK_PERCENTAGE));
	sLog->outDebug(LOG_FILTER_UNITS,
			"MIN_DAMAGE is: \t\t%f\tMAX_DAMAGE is: \t\t%f",
			GetFloatValue(UNIT_FIELD_MINDAMAGE),
			GetFloatValue(UNIT_FIELD_MAXDAMAGE));
	sLog->outDebug(LOG_FILTER_UNITS,
			"MIN_OFFHAND_DAMAGE is: \t%f\tMAX_OFFHAND_DAMAGE is: \t%f",
			GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE),
			GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE));
	sLog->outDebug(LOG_FILTER_UNITS,
			"MIN_RANGED_DAMAGE is: \t%f\tMAX_RANGED_DAMAGE is: \t%f",
			GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE),
			GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE));
	sLog->outDebug(LOG_FILTER_UNITS,
			"ATTACK_TIME is: \t%u\t\tRANGE_ATTACK_TIME is: \t%u",
			GetAttackTime(BASE_ATTACK), GetAttackTime(RANGED_ATTACK));
}

/*********************************************************/
/***               FLOOD FILTER SYSTEM                 ***/
/*********************************************************/

void Player::UpdateSpeakTime() {
	// ignore chat spam protection for GMs in any mode
	if (GetSession()->GetSecurity() > SEC_PLAYER)
		return;

	time_t current = time(NULL);
	if (m_speakTime > current) {
		uint32 max_count = sWorld->getIntConfig(CONFIG_CHATFLOOD_MESSAGE_COUNT);
		if (!max_count)
			return;

		++m_speakCount;
		if (m_speakCount >= max_count) {
			// prevent overwrite mute time, if message send just before mutes set, for example.
			time_t new_mute = current
					+ sWorld->getIntConfig(CONFIG_CHATFLOOD_MUTE_TIME);
			if (GetSession()->m_muteTime < new_mute)
				GetSession()->m_muteTime = new_mute;

			m_speakCount = 0;
		}
	} else
		m_speakCount = 0;

	m_speakTime = current
			+ sWorld->getIntConfig(CONFIG_CHATFLOOD_MESSAGE_DELAY);
}

bool Player::CanSpeak() const {
	return GetSession()->m_muteTime <= time(NULL);
}

/*********************************************************/
/***              LOW LEVEL FUNCTIONS:Notifiers        ***/
/*********************************************************/

void Player::SendAttackSwingNotInRange() {
	WorldPacket data(SMSG_ATTACKSWING_NOTINRANGE, 0);
	GetSession()->SendPacket(&data);
}

void Player::SavePositionInDB(uint32 mapid, float x, float y, float z, float o,
		uint32 zone, uint64 guid) {
	std::ostringstream ss;
	ss << "UPDATE characters SET position_x='" << x << "', position_y='" << y
			<< "', position_z='" << z << "', orientation='" << o << "', map='"
			<< mapid << "', zone='" << zone
			<< "', trans_x='0', trans_y='0', trans_z='0', "
			<< "transguid='0', taxi_path='' WHERE guid='" << GUID_LOPART(guid)
			<< "'";
	sLog->outDebug(LOG_FILTER_UNITS, "%s", ss.str().c_str());
	CharacterDatabase.Execute(ss.str().c_str());
}

void Player::SetUInt32ValueInArray(Tokens& tokens, uint16 index, uint32 value) {
	char buf[11];
	snprintf(buf, 11, "%u", value);

	if (index >= tokens.size())
		return;

	tokens[index] = buf;
}

void Player::Customize(uint64 guid, uint8 gender, uint8 skin, uint8 face,
		uint8 hairStyle, uint8 hairColor, uint8 facialHair) {
	//                                                     0
	QueryResult result = CharacterDatabase.PQuery(
			"SELECT playerBytes2 FROM characters WHERE guid = '%u'",
			GUID_LOPART(guid));
	if (!result)
		return;

	Field* fields = result->Fetch();

	uint32 player_bytes2 = fields[0].GetUInt32();
	player_bytes2 &= ~0xFF;
	player_bytes2 |= facialHair;

	CharacterDatabase.PExecute(
			"UPDATE characters SET gender = '%u', playerBytes = '%u', playerBytes2 = '%u' WHERE guid = '%u'",
			gender, skin | (face << 8) | (hairStyle << 16) | (hairColor << 24),
			player_bytes2, GUID_LOPART(guid));
}

void Player::SendAttackSwingDeadTarget() {
	WorldPacket data(SMSG_ATTACKSWING_DEADTARGET, 0);
	GetSession()->SendPacket(&data);
}

void Player::SendAttackSwingCantAttack() {
	WorldPacket data(SMSG_ATTACKSWING_CANT_ATTACK, 0);
	GetSession()->SendPacket(&data);
}

void Player::SendAttackSwingCancelAttack() {
	WorldPacket data(SMSG_CANCEL_COMBAT, 0);
	GetSession()->SendPacket(&data);
}

void Player::SendAttackSwingBadFacingAttack() {
	WorldPacket data(SMSG_ATTACKSWING_BADFACING, 0);
	GetSession()->SendPacket(&data);
}

void Player::SendAutoRepeatCancel(Unit *target) {
	WorldPacket data(SMSG_CANCEL_AUTO_REPEAT, target->GetPackGUID().size());
	data.append(target->GetPackGUID()); // may be it's target guid
	GetSession()->SendPacket(&data);
}

void Player::SendExplorationExperience(uint32 Area, uint32 Experience) {
	WorldPacket data(SMSG_EXPLORATION_EXPERIENCE, 8);
	data << uint32(Area);
	data << uint32(Experience);
	GetSession()->SendPacket(&data);
}

void Player::SendDungeonDifficulty(bool IsInGroup) {
	uint8 val = 0x00000001;
	WorldPacket data(MSG_SET_DUNGEON_DIFFICULTY, 12);
	data << (uint32) GetDungeonDifficulty();
	data << uint32(val);
	data << uint32(IsInGroup);
	GetSession()->SendPacket(&data);
}

void Player::SendRaidDifficulty(bool IsInGroup, int32 forcedDifficulty) {
	uint8 val = 0x00000001;
	WorldPacket data(MSG_SET_RAID_DIFFICULTY, 12);
	data
			<< uint32(
					forcedDifficulty == -1 ?
							GetRaidDifficulty() : forcedDifficulty);
	data << uint32(val);
	data << uint32(IsInGroup);
	GetSession()->SendPacket(&data);
}

void Player::SendResetFailedNotify(uint32 mapid) {
	WorldPacket data(SMSG_RESET_FAILED_NOTIFY, 4);
	data << uint32(mapid);
	GetSession()->SendPacket(&data);
}

/// Reset all solo instances and optionally send a message on success for each
void Player::ResetInstances(uint8 method, bool isRaid) {
	// method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_JOIN

	// we assume that when the difficulty changes, all instances that can be reset will be
	Difficulty diff = GetDifficulty(isRaid);

	for (BoundInstancesMap::iterator itr = m_boundInstances[diff].begin();
			itr != m_boundInstances[diff].end();) {
		InstanceSave *p = itr->second.save;
		const MapEntry *entry = sMapStore.LookupEntry(itr->first);
		if (!entry || entry->IsRaid() != isRaid || !p->CanReset()) {
			++itr;
			continue;
		}

		if (method == INSTANCE_RESET_ALL) {
			// the "reset all instances" method can only reset normal maps
			if (entry->map_type == MAP_RAID
					|| diff == DUNGEON_DIFFICULTY_HEROIC) {
				++itr;
				continue;
			}
		}

		// if the map is loaded, reset it
		Map *map = sMapMgr->FindMap(p->GetMapId(), p->GetInstanceId());
		if (map && map->IsDungeon())
			if (!((InstanceMap*) map)->Reset(method)) {
				++itr;
				continue;
			}

		// since this is a solo instance there should not be any players inside
		if (method == INSTANCE_RESET_ALL
				|| method == INSTANCE_RESET_CHANGE_DIFFICULTY)
			SendResetInstanceSuccess(p->GetMapId());

		p->DeleteFromDB();
		m_boundInstances[diff].erase(itr++);

		// the following should remove the instance save from the manager and delete it as well
		p->RemovePlayer(this);
	}
}

void Player::SendResetInstanceSuccess(uint32 MapId) {
	WorldPacket data(SMSG_INSTANCE_RESET, 4);
	data << uint32(MapId);
	GetSession()->SendPacket(&data);
}

void Player::SendResetInstanceFailed(uint32 reason, uint32 MapId) {
	// TODO: find what other fail reasons there are besides players in the instance
	WorldPacket data(SMSG_INSTANCE_RESET_FAILED, 4);
	data << uint32(reason);
	data << uint32(MapId);
	GetSession()->SendPacket(&data);
}

/*********************************************************/
/***              Update timers                        ***/
/*********************************************************/

///checks the 15 afk reports per 5 minutes limit
void Player::UpdateAfkReport(time_t currTime) {
	if (m_bgData.bgAfkReportedTimer <= currTime) {
		m_bgData.bgAfkReportedCount = 0;
		m_bgData.bgAfkReportedTimer = currTime + 5 * MINUTE;
	}
}

void Player::UpdateContestedPvP(uint32 diff) {
	if (!m_contestedPvPTimer || isInCombat())
		return;
	if (m_contestedPvPTimer <= diff) {
		ResetContestedPvP();
	} else
		m_contestedPvPTimer -= diff;
}

void Player::UpdatePvPFlag(time_t currTime) {
	if (!IsPvP() || InBattleground() || InArena())
		return;
	if (pvpInfo.endTimer == 0 || currTime < (pvpInfo.endTimer + 300))
		return;

	UpdatePvP(false);
}

void Player::UpdateDuelFlag(time_t currTime) {
	if (!duel || duel->startTimer == 0 || currTime < duel->startTimer + 3)
		return;

	sScriptMgr->OnPlayerDuelStart(this, duel->opponent);

	SetUInt32Value(PLAYER_DUEL_TEAM, 1);
	duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 2);

	duel->startTimer = 0;
	duel->startTime = currTime;
	duel->opponent->duel->startTimer = 0;
	duel->opponent->duel->startTime = currTime;
}

Pet* Player::GetPet() const {
	if (uint64 pet_guid = GetPetGUID()) {
		if (!IS_PET_GUID(pet_guid))
			return NULL;

		Pet* pet = ObjectAccessor::GetPet(*this, pet_guid);

		if (!pet)
			return NULL;

		if (IsInWorld() && pet)
			return pet;

		//there may be a guardian in slot
		//sLog->outError("Player::GetPet: Pet %u not exist.", GUID_LOPART(pet_guid));
		//const_cast<Player*>(this)->SetPetGUID(0);
	}

	return NULL;
}

void Player::RemovePet(Pet* pet, PetSlot mode, bool returnreagent) {
	if (!pet)
		pet = GetPet();

	if (pet) {
		sLog->outDebug(LOG_FILTER_UNITS, "RemovePet %u, %u, %u",
				pet->GetEntry(), mode, returnreagent);

		if (pet->m_removed)
			return;
	}

	if (mode == PET_SLOT_ACTUAL_PET_SLOT)
		mode = m_currentPetSlot;

	if (!pet || pet->GetOwnerGUID() != GetGUID())
		return;

	if (mode >= PET_SLOT_HUNTER_FIRST && mode <= PET_SLOT_HUNTER_LAST
			&& pet->getPetType() != HUNTER_PET)
		assert(false); //debug code.
	/*if (mode == PET_SLOT_OTHER_PET && pet->getPetType() == HUNTER_PET)
	 assert(false); //debug code. */

	pet->CombatStop();

	switch (pet->GetEntry()) {
	//warlock pets except imp are removed(?) when logging out
	case 1860:
	case 1863:
	case 417:
	case 17252:
		mode = PET_SLOT_DELETED;
		break;
	}

	// only if current pet in slot
	pet->SavePetToDB(mode);

	if (pet->getPetType() != HUNTER_PET)
		SetMinion(pet, false, PET_SLOT_UNK_SLOT);
	else
		SetMinion(pet, false, PET_SLOT_ACTUAL_PET_SLOT);

	pet->AddObjectToRemoveList();
	pet->m_removed = true;

	if (pet->isControlled()) {
		WorldPacket data(SMSG_PET_SPELLS, 8);
		data << uint64(0);
		GetSession()->SendPacket(&data);

		if (GetGroup())
			SetGroupUpdateFlag(GROUP_UPDATE_PET);
	}
}

void Player::StopCastingCharm() {
	Unit* charm = GetCharm();
	if (!charm)
		return;

	if (charm->GetTypeId() == TYPEID_UNIT) {
		if (charm->ToCreature()->HasUnitTypeMask(UNIT_MASK_PUPPET))
			((Puppet*) charm)->UnSummon();
		else if (charm->IsVehicle())
			ExitVehicle();
	}
	if (GetCharmGUID())
		charm->RemoveCharmAuras();

	if (GetCharmGUID()) {
		sLog->outCrash(
				"Player %s (GUID: " UI64FMTD " is not able to uncharm unit (GUID: " UI64FMTD " Entry: %u, Type: %u)",
				GetName(), GetGUID(), GetCharmGUID(), charm->GetEntry(),
				charm->GetTypeId());
		if (charm->GetCharmerGUID()) {
			sLog->outCrash("Charmed unit has charmer guid " UI64FMTD,
					charm->GetCharmerGUID());
			ASSERT(false);
		} else
			SetCharm(charm, false);
	}
}

inline void Player::BuildPlayerChat(WorldPacket *data, uint8 msgtype,
		const std::string& text, uint32 language) const {
	*data << uint8(msgtype);
	*data << uint32(language);
	*data << uint64(GetGUID());
	*data << uint32(language); //language 2.1.0 ?
	*data << uint64(GetGUID());
	*data << uint32(text.length() + 1);
	*data << text;
	*data << uint8(chatTag());
}

void Player::Say(const std::string& text, const uint32 language) {
	std::string _text(text);
	sScriptMgr->OnPlayerChat(this, CHAT_MSG_SAY, language, _text);

	WorldPacket data(SMSG_MESSAGECHAT, 200);
	BuildPlayerChat(&data, CHAT_MSG_SAY, _text, language);
	SendMessageToSetInRange(&data,
			sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), true);
		}

void Player::Yell(const std::string& text, const uint32 language) {
	std::string _text(text);
	sScriptMgr->OnPlayerChat(this, CHAT_MSG_YELL, language, _text);

	WorldPacket data(SMSG_MESSAGECHAT, 200);
	BuildPlayerChat(&data, CHAT_MSG_YELL, _text, language);
	SendMessageToSetInRange(&data,
			sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), true);
		}

void Player::TextEmote(const std::string& text) {
	std::string _text(text);
	sScriptMgr->OnPlayerChat(this, CHAT_MSG_EMOTE, LANG_UNIVERSAL, _text);

	WorldPacket data(SMSG_MESSAGECHAT, 200);
	BuildPlayerChat(&data, CHAT_MSG_EMOTE, _text, LANG_UNIVERSAL);
	SendMessageToSetInRange(&data,
			sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), true, !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT));
}

void Player::Whisper(const std::string& text, uint32 language,
		uint64 receiver) {
	if (language != LANG_ADDON) // if not addon data
		language = LANG_UNIVERSAL; // whispers should always be readable

	Player *rPlayer = sObjectMgr->GetPlayer(receiver);

	std::string _text(text);
	sScriptMgr->OnPlayerChat(this, CHAT_MSG_WHISPER, language, _text, rPlayer);

	// when player you are whispering to is dnd, he cannot receive your message, unless you are in gm mode
	if (!rPlayer->isDND() || isGameMaster()) {
		WorldPacket data(SMSG_MESSAGECHAT, 200);
		BuildPlayerChat(&data, CHAT_MSG_WHISPER, _text, language);
		rPlayer->GetSession()->SendPacket(&data);

		// not send confirmation for addon messages
		if (language != LANG_ADDON) {
			data.Initialize(SMSG_MESSAGECHAT, 200);
			rPlayer->BuildPlayerChat(&data, CHAT_MSG_WHISPER_INFORM, _text,
					language);
			GetSession()->SendPacket(&data);
		}
	} else
		// announce to player that player he is whispering to is dnd and cannot receive his message
		ChatHandler(this).PSendSysMessage(LANG_PLAYER_DND, rPlayer->GetName(),
				rPlayer->dndMsg.c_str());

	if (!isAcceptWhispers() && !isGameMaster() && !rPlayer->isGameMaster()) {
		SetAcceptWhispers(true);
		ChatHandler(this).SendSysMessage(LANG_COMMAND_WHISPERON);
	}

	// announce to player that player he is whispering to is afk
	if (rPlayer->isAFK())
		ChatHandler(this).PSendSysMessage(LANG_PLAYER_AFK, rPlayer->GetName(),
				rPlayer->afkMsg.c_str());

	// if player whisper someone, auto turn of dnd to be able to receive an answer
	if (isDND() && !rPlayer->isGameMaster())
		ToggleDND();
}

void Player::PetSpellInitialize() {
	Pet* pet = GetPet();

	if (!pet)
		return;

	sLog->outDebug(LOG_FILTER_PETS, "Pet Spells Groups");

	CharmInfo *charmInfo = pet->GetCharmInfo();

	WorldPacket data(SMSG_PET_SPELLS,
			8 + 2 + 4 + 4 + 4 * MAX_UNIT_ACTION_BAR_INDEX + 1 + 1);
	data << uint64(pet->GetGUID());
	data << uint16(pet->GetCreatureInfo()->family); // creature family (required for pet talents)
	data << uint32(0);
	data << uint8(pet->GetReactState()) << uint8(charmInfo->GetCommandState())
			<< uint16(0);

	// action bar loop
	charmInfo->BuildActionBar(&data);

	size_t spellsCountPos = data.wpos();

	// spells count
	uint8 addlist = 0;
	data << uint8(addlist); // placeholder

	if (pet->IsPermanentPetFor(this)) {
		// spells loop
		for (PetSpellMap::iterator itr = pet->m_spells.begin();
				itr != pet->m_spells.end(); ++itr) {
			if (itr->second.state == PETSPELL_REMOVED)
				continue;

			data
					<< uint32(
							MAKE_UNIT_ACTION_BUTTON(itr->first, itr->second.active));
			++addlist;
		}
	}

	data.put<uint8>(spellsCountPos, addlist);

	uint8 cooldownsCount = pet->m_CreatureSpellCooldowns.size()
			+ pet->m_CreatureCategoryCooldowns.size();
	data << uint8(cooldownsCount);

	time_t curTime = time(NULL);

	for (CreatureSpellCooldowns::const_iterator itr =
			pet->m_CreatureSpellCooldowns.begin();
			itr != pet->m_CreatureSpellCooldowns.end(); ++itr) {
		time_t cooldown =
				(itr->second > curTime) ?
						(itr->second - curTime) * IN_MILLISECONDS : 0;

		data << uint32(itr->first); // spellid
		data << uint16(0); // spell category?
		data << uint32(cooldown); // cooldown
		data << uint32(0); // category cooldown
	}

	for (CreatureSpellCooldowns::const_iterator itr =
			pet->m_CreatureCategoryCooldowns.begin();
			itr != pet->m_CreatureCategoryCooldowns.end(); ++itr) {
		time_t cooldown =
				(itr->second > curTime) ?
						(itr->second - curTime) * IN_MILLISECONDS : 0;

		data << uint32(itr->first); // spellid
		data << uint16(0); // spell category?
		data << uint32(0); // cooldown
		data << uint32(cooldown); // category cooldown
	}

	data.hexlike();

	GetSession()->SendPacket(&data);
}

void Player::PossessSpellInitialize() {
	Unit* charm = GetCharm();
	if (!charm)
		return;

	CharmInfo *charmInfo = charm->GetCharmInfo();

	if (!charmInfo) {
		sLog->outError(
				"Player::PossessSpellInitialize(): charm ("UI64FMTD") has no charminfo!",
				charm->GetGUID());
		return;
	}

	WorldPacket data(SMSG_PET_SPELLS,
			8 + 2 + 4 + 4 + 4 * MAX_UNIT_ACTION_BAR_INDEX + 1 + 1);
	data << uint64(charm->GetGUID());
	data << uint16(0);
	data << uint32(0);
	data << uint32(0);

	charmInfo->BuildActionBar(&data);

	data << uint8(0); // spells count
	data << uint8(0); // cooldowns count

	GetSession()->SendPacket(&data);
}

void Player::VehicleSpellInitialize() {
	Creature* veh = GetVehicleCreatureBase();
	if (!veh)
		return;

	// GetPosition_ is not a member of 'Vehicle', SetPosition is a member of 'Player': SetPosition(GetVehicle()->GetPositionX(), GetVehicle()->GetPositionY(), GetVehicle()->GetPositionZ(), GetVehicle()->GetOrientation());

	// GetPosition_ is not a member of 'Vehicle', SetPosition is a member of 'Player': SetPosition(GetVehicle()->GetPositionX(), GetVehicle()->GetPositionY(), GetVehicle()->GetPositionZ(), GetVehicle()->GetOrientation());

	WorldPacket data(SMSG_PET_SPELLS, 8 + 2 + 4 + 4 + 4 * 10 + 1 + 1);
	data << uint64(veh->GetGUID());
	data << uint16(0);
	data << uint32(0);
	data << uint32(0x00000101);

	for (uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i) {
		uint32 spellId = veh->ToCreature()->m_spells[i];
		if (!spellId)
			continue;

		SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
		if (!spellInfo)
			continue;

		ConditionList conditions = sConditionMgr->GetConditionsForVehicleSpell(
				veh->ToCreature()->GetEntry(), spellId);
		if (!sConditionMgr->IsPlayerMeetToConditions(this, conditions)) {
			sLog->outDebug(
					LOG_FILTER_CONDITIONSYS,
					"VehicleSpellInitialize: conditions not met for Vehicle entry %u spell %u",
					veh->ToCreature()->GetEntry(), spellId);
			continue;
		}
		if (IsPassiveSpell(spellId)) {
			veh->CastSpell(veh, spellId, true);
			data << uint16(0) << uint8(0) << uint8(i + 8);
		} else
			data << uint32(MAKE_UNIT_ACTION_BUTTON(spellId, i+8));
	}

	for (uint32 i = CREATURE_MAX_SPELLS; i < MAX_SPELL_CONTROL_BAR; ++i)
		data << uint16(0) << uint8(0) << uint8(i + 8);

	data << uint8(0);
	data << uint8(0);
	GetSession()->SendPacket(&data);
}

void Player::CharmSpellInitialize() {
	Unit* charm = GetFirstControlled();
	if (!charm)
		return;

	CharmInfo *charmInfo = charm->GetCharmInfo();
	if (!charmInfo) {
		sLog->outError(
				"Player::CharmSpellInitialize(): the player's charm ("UI64FMTD") has no charminfo!",
				charm->GetGUID());
		return;
	}

	uint8 addlist = 0;
	if (charm->GetTypeId() != TYPEID_PLAYER) {
		//CreatureInfo const *cinfo = charm->ToCreature()->GetCreatureInfo();
		//if (cinfo && cinfo->type == CREATURE_TYPE_DEMON && getClass() == CLASS_WARLOCK)
		{
			for (uint32 i = 0; i < MAX_SPELL_CHARM; ++i)
				if (charmInfo->GetCharmSpell(i)->GetAction())
					++addlist;
		}
	}

	WorldPacket data(
			SMSG_PET_SPELLS,
			8 + 2 + 4 + 4 + 4 * MAX_UNIT_ACTION_BAR_INDEX + 1 + 4 * addlist
					+ 1);
	data << uint64(charm->GetGUID());
	data << uint16(0);
	data << uint32(0);

	if (charm->GetTypeId() != TYPEID_PLAYER)
		data << uint8(charm->ToCreature()->GetReactState())
				<< uint8(charmInfo->GetCommandState()) << uint16(0);
	else
		data << uint8(0) << uint8(0) << uint16(0);

	charmInfo->BuildActionBar(&data);

	data << uint8(addlist);

	if (addlist) {
		for (uint32 i = 0; i < MAX_SPELL_CHARM; ++i) {
			CharmSpellEntry *cspell = charmInfo->GetCharmSpell(i);
			if (cspell->GetAction())
				data << uint32(cspell->packedData);
		}
	}

	data << uint8(0); // cooldowns count

	GetSession()->SendPacket(&data);
}

void Player::SendRemoveControlBar() {
	WorldPacket data(SMSG_PET_SPELLS, 8);
	data << uint64(0);
	GetSession()->SendPacket(&data);
}

bool Player::IsAffectedBySpellmod(SpellEntry const *spellInfo,
		SpellModifier *mod, Spell * spell) {
	if (!mod || !spellInfo)
		return false;

	// Mod out of charges
	if (spell && mod->charges == -1
			&& spell->m_appliedMods.find(mod->ownerAura)
					== spell->m_appliedMods.end())
		return false;

	// +duration to infinite duration spells making them limited
	if (mod->op == SPELLMOD_DURATION && GetSpellDuration(spellInfo) == -1)
		return false;

	return sSpellMgr->IsAffectedByMod(spellInfo, mod);
}

void Player::AddSpellMod(SpellModifier* mod, bool apply) {
	sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Player::AddSpellMod %d",
			mod->spellId);
	bool isFlat = mod->type == SPELLMOD_FLAT;
	uint32 Opcode =
			(isFlat) ?
					SMSG_SET_FLAT_SPELL_MODIFIER : SMSG_SET_PCT_SPELL_MODIFIER;

	WorldPacket data(Opcode);
	data << uint32(1); //number of spell mod to add
	size_t wpos_count2 = data.wpos();
	uint32 count2 = 0;
	data << uint32(count2);
	data << uint8(mod->op);
	int i = 0;
	flag96 _mask = 0;
	for (int eff = 0; eff < 96; ++eff) {
		if (eff != 0 && eff % 32 == 0)
			_mask[i++] = 0;

		_mask[i] = uint32(1) << (eff - (32 * i));
		if (mod->mask & _mask) {
			int32 val = 0;
			for (SpellModList::iterator itr = m_spellMods[mod->op].begin();
					itr != m_spellMods[mod->op].end(); ++itr) {
				if ((*itr)->type == mod->type && (*itr)->mask & _mask)
					val += (*itr)->value;
			}
			val += apply ? mod->value : -(mod->value);
			data << uint8(eff);
			if (isFlat)
				data << int32(val);
			else
				data << float(val);
			count2++;
		}
	}
	data.put(wpos_count2, count2);

	SendDirectMessage(&data);

	/*int i = 0;
	 flag96 _mask = 0;
	 for (int eff = 0; eff < 96; ++eff)
	 {
	 if (eff != 0 && eff%32 == 0)
	 _mask[i++] = 0;

	 _mask[i] = uint32(1) << (eff-(32*i));
	 if (mod->mask & _mask)
	 {
	 int32 val = 0;
	 for (SpellModList::iterator itr = m_spellMods[mod->op].begin(); itr != m_spellMods[mod->op].end(); ++itr)
	 {
	 if ((*itr)->type == mod->type && (*itr)->mask & _mask)
	 val += (*itr)->value;
	 }
	 val += apply ? mod->value : -(mod->value);
	 WorldPacket data(Opcode, (1+1+4));
	 data << uint8(eff);
	 data << uint8(mod->op);
	 data << int32(val);
	 //SendDirectMessage(&data);
	 }
	 }*/

	if (apply)
		m_spellMods[mod->op].push_back(mod);
	else {
		m_spellMods[mod->op].remove(mod);
		// mods bound to aura will be removed in AuraEffect::~AuraEffect
		if (!mod->ownerAura)
			delete mod;
	}
}

// Restore spellmods in case of failed cast
void Player::RestoreSpellMods(Spell *spell, uint32 ownerAuraId) {
	if (!spell || spell->m_appliedMods.empty())
		return;

	for (uint8 i = 0; i < MAX_SPELLMOD; ++i) {
		for (SpellModList::iterator itr = m_spellMods[i].begin();
				itr != m_spellMods[i].end(); ++itr) {
			SpellModifier *mod = *itr;

			// spellmods without aura set cannot be charged
			if (!mod->ownerAura || !mod->ownerAura->GetCharges())
				continue;

			// Restore only specific owner aura mods
			if (ownerAuraId
					&& (ownerAuraId != mod->ownerAura->GetSpellProto()->Id))
				continue;

			// check if mod affected this spell
			Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(
					mod->ownerAura);
			if (iterMod == spell->m_appliedMods.end())
				continue;

			// remove from list
			spell->m_appliedMods.erase(iterMod);

			// add mod charges back to mod
			if (mod->charges == -1)
				mod->charges = 1;
			else
				mod->charges++;

			// Do not set more spellmods than avalible
			if (mod->ownerAura->GetCharges() < mod->charges)
				mod->charges = mod->ownerAura->GetCharges();

			// Skip this check for now - aura charges may change due to various reason
			// TODO: trac these changes correctly
			//ASSERT (mod->ownerAura->GetCharges() <= mod->charges);
		}
	}
}

void Player::RemoveSpellMods(Spell * spell) {
	if (!spell)
		return;

	if (spell->m_appliedMods.empty())
		return;

	for (uint8 i = 0; i < MAX_SPELLMOD; ++i) {
		for (SpellModList::iterator itr = m_spellMods[i].begin();
				itr != m_spellMods[i].end();) {
			SpellModifier *mod = *itr;
			++itr;

			// spellmods without aura set cannot be charged
			if (!mod->ownerAura || !mod->ownerAura->GetCharges())
				continue;

			// check if mod affected this spell
			Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(
					mod->ownerAura);
			if (iterMod == spell->m_appliedMods.end())
				continue;

			// remove from list
			spell->m_appliedMods.erase(iterMod);

			if (mod->ownerAura->DropCharge())
				itr = m_spellMods[i].begin();
		}
	}
}

void Player::DropModCharge(SpellModifier * mod, Spell * spell) {
	if (spell && mod->ownerAura && mod->charges > 0) {
		--mod->charges;
		if (mod->charges == 0) {
			mod->charges = -1;
		}
		spell->m_appliedMods.insert(mod->ownerAura);
	}
}

void Player::SetSpellModTakingSpell(Spell * spell, bool apply) {
	if (!spell || (m_spellModTakingSpell && m_spellModTakingSpell != spell))
		return;

	if (apply && spell->getState() == SPELL_STATE_FINISHED)
		return;

	m_spellModTakingSpell = apply ? spell : NULL;
}

// send Proficiency
void Player::SendProficiency(ItemClass itemClass, uint32 itemSubclassMask) {
	WorldPacket data(SMSG_SET_PROFICIENCY, 1 + 4);
	data << uint8(itemClass) << uint32(itemSubclassMask);
	GetSession()->SendPacket(&data);
}

void Player::RemovePetitionsAndSigns(uint64 guid, uint32 type) {
	QueryResult result = QueryResult(NULL);
	if (type == 10)
		result =
				CharacterDatabase.PQuery(
						"SELECT ownerguid, petitionguid FROM petition_sign WHERE playerguid = '%u'",
						GUID_LOPART(guid));
	else
		result =
				CharacterDatabase.PQuery(
						"SELECT ownerguid, petitionguid FROM petition_sign WHERE playerguid = '%u' AND type = '%u'",
						GUID_LOPART(guid), type);
	if (result) {
		do // this part effectively does nothing, since the deletion / modification only takes place _after_ the PetitionQuery. Though I don't know if the result remains intact if I execute the delete query beforehand.
		{ // and SendPetitionQueryOpcode reads data from the DB
			Field *fields = result->Fetch();
			uint64 ownerguid =
					MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
			uint64 petitionguid =
					MAKE_NEW_GUID(fields[1].GetUInt32(), 0, HIGHGUID_ITEM);

			// send update if charter owner in game
			Player* owner = sObjectMgr->GetPlayer(ownerguid);
			if (owner)
				owner->GetSession()->SendPetitionQueryOpcode(petitionguid);
		} while (result->NextRow());

		if (type == 10)
			CharacterDatabase.PExecute(
					"DELETE FROM petition_sign WHERE playerguid = '%u'",
					GUID_LOPART(guid));
		else
			CharacterDatabase.PExecute(
					"DELETE FROM petition_sign WHERE playerguid = '%u' AND type = '%u'",
					GUID_LOPART(guid), type);
	}

	SQLTransaction trans = CharacterDatabase.BeginTransaction();
	if (type == 10) {
		trans->PAppend("DELETE FROM petition WHERE ownerguid = '%u'",
				GUID_LOPART(guid));
		trans->PAppend("DELETE FROM petition_sign WHERE ownerguid = '%u'",
				GUID_LOPART(guid));
	} else {
		trans->PAppend("DELETE FROM petition WHERE ownerguid = '%u'",
				GUID_LOPART(guid));
		trans->PAppend(
				"DELETE FROM petition_sign WHERE ownerguid = '%u' AND type = '%u'",
				GUID_LOPART(guid), type);
	}
	CharacterDatabase.CommitTransaction(trans);
}

void Player::LeaveAllArenaTeams(uint64 guid) {
	PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(
			CHAR_LOAD_PLAYER_ARENA_TEAMS);
	stmt->setUInt32(0, GUID_LOPART(guid));
	PreparedQueryResult result = CharacterDatabase.Query(stmt);

	if (!result)
		return;

	do {
		Field* fields = result->Fetch();
		uint32 arenaTeamId = fields[0].GetUInt32();
		if (arenaTeamId != 0) {
			ArenaTeam* arenaTeam = sObjectMgr->GetArenaTeamById(arenaTeamId);
			if (arenaTeam)
				arenaTeam->DelMember(guid, true);
		}
	} while (result->NextRow());
}

void Player::SetRestBonus(float rest_bonus_new) {
	// Prevent resting on max level
	if (getLevel() >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
		rest_bonus_new = 0;

	if (rest_bonus_new < 0)
		rest_bonus_new = 0;

	float rest_bonus_max = (float) GetUInt32Value(PLAYER_NEXT_LEVEL_XP) * 1.5f
			/ 2;

	if (rest_bonus_new > rest_bonus_max)
		m_rest_bonus = rest_bonus_max;
	else
		m_rest_bonus = rest_bonus_new;

	// update data for client
	if (m_rest_bonus > 10)
		SetByteValue(PLAYER_BYTES_2, 3, 0x01); // Set Reststate = Rested
	else if (m_rest_bonus <= 1)
		SetByteValue(PLAYER_BYTES_2, 3, 0x02); // Set Reststate = Normal

	//RestTickUpdate
	SetUInt32Value(PLAYER_REST_STATE_EXPERIENCE, uint32(m_rest_bonus));
}

bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes,
		Creature* npc /*= NULL*/, uint32 spellid /*= 0*/) {
	if (nodes.size() < 2)
		return false;

	// not let cheating with start flight in time of logout process || while in combat || has type state: stunned || has type state: root
	if (GetSession()->isLogingOut() || isInCombat()
			|| HasUnitState(UNIT_STAT_STUNNED)
			|| HasUnitState(UNIT_STAT_ROOT)) {
		WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
		data << uint32(ERR_TAXIPLAYERBUSY);
		GetSession()->SendPacket(&data);
		return false;
	}

	if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
		return false;

	// taximaster case
	if (npc) {
		// not let cheating with start flight mounted
		if (IsMounted()) {
			WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
			data << uint32(ERR_TAXIPLAYERALREADYMOUNTED);
			GetSession()->SendPacket(&data);
			return false;
		}

		if (IsInDisallowedMountForm()) {
			WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
			data << uint32(ERR_TAXIPLAYERSHAPESHIFTED);
			GetSession()->SendPacket(&data);
			return false;
		}

		// not let cheating with start flight in time of logout process || if casting not finished || while in combat || if not use Spell's with EffectSendTaxi
		if (IsNonMeleeSpellCasted(false)) {
			WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
			data << uint32(ERR_TAXIPLAYERBUSY);
			GetSession()->SendPacket(&data);
			return false;
		}
	}
	// cast case or scripted call case
	else {
		RemoveAurasByType(SPELL_AURA_MOUNTED);

		if (IsInDisallowedMountForm())
			RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);

		if (Spell* spell = GetCurrentSpell(CURRENT_GENERIC_SPELL))
			if (spell->m_spellInfo->Id != spellid)
				InterruptSpell(CURRENT_GENERIC_SPELL, false);

		InterruptSpell(CURRENT_AUTOREPEAT_SPELL, false);

		if (Spell* spell = GetCurrentSpell(CURRENT_CHANNELED_SPELL))
			if (spell->m_spellInfo->Id != spellid)
				InterruptSpell(CURRENT_CHANNELED_SPELL, true);
	}

	uint32 sourcenode = nodes[0];

	// starting node too far away (cheat?)
	TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(sourcenode);
	if (!node) {
		WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
		data << uint32(ERR_TAXINOSUCHPATH);
		GetSession()->SendPacket(&data);
		return false;
	}

	// check node starting pos data set case if provided
	if (node->x != 0.0f || node->y != 0.0f || node->z != 0.0f) {
		if (node->map_id != GetMapId()
				|| (node->x - GetPositionX()) * (node->x - GetPositionX())
						+ (node->y - GetPositionY())
								* (node->y - GetPositionY())
						+ (node->z - GetPositionZ())
								* (node->z - GetPositionZ())
						> (2 * INTERACTION_DISTANCE)
								* (2 * INTERACTION_DISTANCE)
								* (2 * INTERACTION_DISTANCE)) {
			WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
			data << uint32(ERR_TAXITOOFARAWAY);
			GetSession()->SendPacket(&data);
			return false;
		}
	}
	// node must have pos if taxi master case (npc != NULL)
	else if (npc) {
		WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
		data << uint32(ERR_TAXIUNSPECIFIEDSERVERERROR);
		GetSession()->SendPacket(&data);
		return false;
	}

	// Prepare to flight start now

	// stop combat at start taxi flight if any
	CombatStop();

	StopCastingCharm();
	StopCastingBindSight();
	ExitVehicle();

	// stop trade (client cancel trade at taxi map open but cheating tools can be used for reopen it)
	TradeCancel(true);

	// clean not finished taxi path if any
	m_taxi.ClearTaxiDestinations();

	// fill destinations path tail
	uint32 sourcepath = 0;
	uint32 totalcost = 0;

	uint32 prevnode = sourcenode;
	uint32 lastnode = 0;

	for (uint32 i = 1; i < nodes.size(); ++i) {
		uint32 path, cost;

		lastnode = nodes[i];
		sObjectMgr->GetTaxiPath(prevnode, lastnode, path, cost);

		if (!path) {
			m_taxi.ClearTaxiDestinations();
			return false;
		}

		totalcost += cost;

		if (prevnode == sourcenode)
			sourcepath = path;

		m_taxi.AddTaxiDestination(lastnode);

		prevnode = lastnode;
	}

	// get mount model (in case non taximaster (npc == NULL) allow more wide lookup)
	//
	// Hack-Fix for Alliance not being able to use Acherus taxi. There is
	// only one mount ID for both sides. Probably not good to use 315 in case DBC nodes
	// change but I couldn't find a suitable alternative. OK to use class because only DK
	// can use this taxi.
	uint32 mount_display_id = sObjectMgr->GetTaxiMountDisplayId(
			sourcenode,
			GetTeam(),
			npc == NULL
					|| (sourcenode == 315 && getClass() == CLASS_DEATH_KNIGHT));

	// in spell case allow 0 model
	if ((mount_display_id == 0 && spellid == 0) || sourcepath == 0) {
		WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
		data << uint32(ERR_TAXIUNSPECIFIEDSERVERERROR);
		GetSession()->SendPacket(&data);
		m_taxi.ClearTaxiDestinations();
		return false;
	}

	uint32 money = GetMoney();

	if (npc)
		totalcost = (uint32) ceil(totalcost * GetReputationPriceDiscount(npc));

	if (money < totalcost) {
		WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
		data << uint32(ERR_TAXINOTENOUGHMONEY);
		GetSession()->SendPacket(&data);
		m_taxi.ClearTaxiDestinations();
		return false;
	}

	//Checks and preparations done, DO FLIGHT
	ModifyMoney(-(int32) totalcost);
	GetAchievementMgr().UpdateAchievementCriteria(
			ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING, totalcost);
	GetAchievementMgr().UpdateAchievementCriteria(
			ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN, 1);

	// prevent stealth flight
	//RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK);

	if (sWorld->getBoolConfig(CONFIG_INSTANT_TAXI)) {
		TaxiNodesEntry const* lastnode = sTaxiNodesStore.LookupEntry(
				nodes[nodes.size() - 1]);
		m_taxi.ClearTaxiDestinations();
		TeleportTo(lastnode->map_id, lastnode->x, lastnode->y, lastnode->z,
				GetOrientation());
		return false;
	} else {
		WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
		data << uint32(ERR_TAXIOK);
		GetSession()->SendPacket(&data);
		sLog->outDebug(LOG_FILTER_NETWORKIO,
				"World: Sent SMSG_ACTIVATETAXIREPLY");
		GetSession()->SendDoFlight(mount_display_id, sourcepath);
	}
	return true;
}

bool Player::ActivateTaxiPathTo(uint32 taxi_path_id, uint32 spellid /*= 0*/) {
	TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(taxi_path_id);
	if (!entry)
		return false;

	std::vector<uint32> nodes;

	nodes.resize(2);
	nodes[0] = entry->from;
	nodes[1] = entry->to;

	return ActivateTaxiPathTo(nodes, NULL, spellid);
}

void Player::CleanupAfterTaxiFlight() {
	m_taxi.ClearTaxiDestinations(); // not destinations, clear source node
	Unmount();
	RemoveFlag(UNIT_FIELD_FLAGS,
			UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
	getHostileRefManager().setOnlineOfflineState(true);
}

void Player::ContinueTaxiFlight() {
	uint32 sourceNode = m_taxi.GetTaxiSource();
	if (!sourceNode)
		return;

	sLog->outDebug(LOG_FILTER_NETWORKIO,
			"World: Restart character %u taxi flight", GetGUIDLow());

	uint32 mountDisplayId = sObjectMgr->GetTaxiMountDisplayId(sourceNode,
			GetTeam(), true);
	uint32 path = m_taxi.GetCurrentTaxiPath();

	// search appropriate start path node
	uint32 startNode = 0;

	TaxiPathNodeList const& nodeList = sTaxiPathNodesByPath[path];

	float distPrev = MAP_SIZE * MAP_SIZE;
	float distNext = (nodeList[0].x - GetPositionX())
			* (nodeList[0].x - GetPositionX())
			+ (nodeList[0].y - GetPositionY())
					* (nodeList[0].y - GetPositionY())
			+ (nodeList[0].z - GetPositionZ())
					* (nodeList[0].z - GetPositionZ());

	for (uint32 i = 1; i < nodeList.size(); ++i) {
		TaxiPathNodeEntry const& node = nodeList[i];
		TaxiPathNodeEntry const& prevNode = nodeList[i - 1];

		// skip nodes at another map
		if (node.mapid != GetMapId())
			continue;

		distPrev = distNext;

		distNext = (node.x - GetPositionX()) * (node.x - GetPositionX())
				+ (node.y - GetPositionY()) * (node.y - GetPositionY())
				+ (node.z - GetPositionZ()) * (node.z - GetPositionZ());

		float distNodes = (node.x - prevNode.x) * (node.x - prevNode.x)
				+ (node.y - prevNode.y) * (node.y - prevNode.y)
				+ (node.z - prevNode.z) * (node.z - prevNode.z);

		if (distNext + distPrev < distNodes) {
			startNode = i;
			break;
		}
	}

	GetSession()->SendDoFlight(mountDisplayId, path, startNode);
}

void Player::ProhibitSpellScholl(SpellSchoolMask idSchoolMask,
		uint32 unTimeMs) {
	// last check 2.0.10
	WorldPacket data(SMSG_SPELL_COOLDOWN, 8 + 1 + m_spells.size() * 8);
	data << uint64(GetGUID());
	data << uint8(0x0); // flags (0x1, 0x2)
	time_t curTime = time(NULL);
	for (PlayerSpellMap::const_iterator itr = m_spells.begin();
			itr != m_spells.end(); ++itr) {
		if (itr->second->state == PLAYERSPELL_REMOVED)
			continue;
		uint32 unSpellId = itr->first;
		SpellEntry const *spellInfo = sSpellStore.LookupEntry(unSpellId);
		if (!spellInfo) {
			ASSERT(spellInfo);
			continue;
		}

		// Not send cooldown for this spells
		if (spellInfo->Attributes & SPELL_ATTR0_DISABLED_WHILE_ACTIVE)
			continue;

		if (spellInfo->PreventionType != SPELL_PREVENTION_TYPE_SILENCE)
			continue;

		if ((idSchoolMask & GetSpellSchoolMask(spellInfo))
				&& GetSpellCooldownDelay(unSpellId) < unTimeMs) {
			data << uint32(unSpellId);
			data << uint32(unTimeMs); // in m.secs
			AddSpellCooldown(unSpellId, 0,
					curTime + unTimeMs / IN_MILLISECONDS);
		}
	}
	GetSession()->SendPacket(&data);
}

void Player::InitDataForForm(bool reapplyMods) {
	ShapeshiftForm form = GetShapeshiftForm();

	SpellShapeshiftFormEntry const* ssEntry =
			sSpellShapeshiftFormStore.LookupEntry(form);
	if (ssEntry && ssEntry->attackSpeed) {
		SetAttackTime(BASE_ATTACK, ssEntry->attackSpeed);
		SetAttackTime(OFF_ATTACK, ssEntry->attackSpeed);
		SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME);
	} else
		SetRegularAttackTime();

	switch (form) {
	case FORM_GHOUL:
	case FORM_CAT: {
		if (getPowerType() != POWER_ENERGY)
			setPowerType(POWER_ENERGY);
		break;
	}
	case FORM_BEAR:
	case FORM_DIREBEAR: {
		if (getPowerType() != POWER_RAGE)
			setPowerType(POWER_RAGE);
		break;
	}
	default: // 0, for example
	{
		ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(
				getClass());
		if (cEntry && cEntry->powerType < MAX_POWERS
				&& uint32(getPowerType()) != cEntry->powerType)
			setPowerType(Powers(cEntry->powerType));
		break;
	}
	}

	// update auras at form change, ignore this at mods reapply (.reset stats/etc) when form not change.
	if (!reapplyMods)
		UpdateEquipSpellsAtFormChange();

	UpdateAttackPowerAndDamage();
	UpdateAttackPowerAndDamage(true);
}

void Player::InitDisplayIds() {
	PlayerInfo const *info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
	if (!info) {
		sLog->outError(
				"Player %u has incorrect race/class pair. Can't init display ids.",
				GetGUIDLow());
		return;
	}

	uint8 gender = getGender();
	switch (gender) {
	case GENDER_FEMALE:
		SetDisplayId(info->displayId_f);
		SetNativeDisplayId(info->displayId_f);
		break;
	case GENDER_MALE:
		SetDisplayId(info->displayId_m);
		SetNativeDisplayId(info->displayId_m);
		break;
	default:
		sLog->outError("Invalid gender %u for player", gender);
		return;
	}
}

inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item,
		uint8 count, uint8 bag, uint8 slot, int32 price,
		ItemPrototype const *pProto, Creature *pVendor,
		VendorItem const* crItem, bool bStore) {
	ItemPosCountVec vDest;
	uint16 uiDest;
	uint8 msg =
			bStore ?
					CanStoreNewItem(bag, slot, vDest, item,
							pProto->BuyCount * count) :
					CanEquipNewItem(slot, uiDest, item, false);
	if (msg != EQUIP_ERR_OK) {
		SendEquipError(msg, NULL, NULL, item);
		return false;
	}

	ModifyMoney(-price);

	if (crItem->ExtendedCost) // case for new honor system
	{
		ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(
				crItem->ExtendedCost);
		for (int i = 0; i < MAX_EXTENDED_COST_ITEMS; ++i) {
			if (iece->RequiredItem[i])
				DestroyItemCount(iece->RequiredItem[i],
						(iece->RequiredItemCount[i] * count), true);
		}

		for (int i = 0; i < MAX_EXTENDED_COST_CURRENCIES; ++i) {
			switch (iece->RequiredCurrency[i]) {
			case NULL:
				break;
			case CURRENCY_TYPE_CONQUEST_POINTS: // There are currencies that include multiplier in dbc
			case CURRENCY_TYPE_HONOR_POINTS:
			case CURRENCY_TYPE_JUSTICE_POINTS:
			case CURRENCY_TYPE_VALOR_POINTS:
				ModifyCurrency(iece->RequiredCurrency[i],
						-int32(iece->RequiredCurrencyCount[i] * count));
				break;
			default: // other ones need multiplier
				ModifyCurrency(
						iece->RequiredCurrency[i],
						-int32(
								iece->RequiredCurrencyCount[i]
										* count * PLAYER_CURRENCY_PRECISION));
			}
		}
	}

	Item* it =
			bStore ?
					StoreNewItem(vDest, item, true) :
					EquipNewItem(uiDest, item, true);
	if (it) {
		uint32 new_count = pVendor->UpdateVendorItemCurrentCount(crItem,
				pProto->BuyCount * count);

		WorldPacket data(SMSG_BUY_ITEM, (8 + 4 + 4 + 4));
		data << uint64(pVendor->GetGUID());
		data << uint32(vendorslot + 1); // numbered from 1 at client
		data << int32(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
		data << uint32(count);
		GetSession()->SendPacket(&data);
		SendNewItem(it, pProto->BuyCount * count, true, false, false);

		if (!bStore)
			AutoUnequipOffhandIfNeed();

		if (pProto->Flags & ITEM_PROTO_FLAG_REFUNDABLE && crItem->ExtendedCost
				&& pProto->GetMaxStackSize() == 1) {
			it->SetFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE);
			it->SetRefundRecipient(GetGUIDLow());
			it->SetPaidMoney(price);
			it->SetPaidExtendedCost(crItem->ExtendedCost);
			it->SaveRefundDataToDB();
			AddRefundReference(it->GetGUIDLow());
		}
	}
	return true;
}

// Return true is the bought item has a max count to force refresh of window by caller
bool Player::BuyItemFromVendorSlot(uint64 vendorguid, uint32 vendorslot,
		uint32 item, uint8 count, uint8 bag, uint8 slot) {
	// cheating attempt
	if (count < 1)
		count = 1;

	// cheating attempt
	if (slot > MAX_BAG_SIZE && slot != NULL_SLOT)
		return false;

	if (!isAlive())
		return false;

	ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(item);
	if (!pProto) {
		SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, item, 0);
		return false;
	}

	Creature *pCreature = GetNPCIfCanInteractWith(vendorguid,
			UNIT_NPC_FLAG_VENDOR);
	if (!pCreature) {
		sLog->outDebug(
				LOG_FILTER_NETWORKIO,
				"World: BuyItemFromVendor - Unit (GUID: %u) not found or you can't interact with him.",
				uint32(GUID_LOPART(vendorguid)));
		SendBuyError(BUY_ERR_DISTANCE_TOO_FAR, NULL, item, 0);
		return false;
	}

	VendorItemData const* vItems = pCreature->GetVendorItems();
	if (!vItems || vItems->Empty()) {
		SendBuyError(BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
		return false;
	}

	if (vendorslot >= vItems->GetItemCount()) {
		SendBuyError(BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
		return false;
	}

	VendorItem const* crItem = vItems->GetItem(vendorslot);
	// store diff item (cheating)
	if (!crItem || crItem->item != item) {
		SendBuyError(BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
		return false;
	}

	if (count == pProto->BuyCount)
		count = 1;

	// check current item amount if it limited
	if (crItem->maxcount != 0) {
		if (pCreature->GetVendorItemCurrentCount(crItem)
				< pProto->BuyCount * count) {
			SendBuyError(BUY_ERR_ITEM_ALREADY_SOLD, pCreature, item, 0);
			return false;
		}
	}

	if (pProto->RequiredReputationFaction
			&& (uint32(GetReputationRank(pProto->RequiredReputationFaction))
					< pProto->RequiredReputationRank)) {
		SendBuyError(BUY_ERR_REPUTATION_REQUIRE, pCreature, item, 0);
		return false;
	}

	if (crItem->ExtendedCost) {
		ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(
				crItem->ExtendedCost);
		if (!iece) {
			sLog->outError("Item %u have wrong ExtendedCost field value %u",
					pProto->ItemId, crItem->ExtendedCost);
			return false;
		}

		// item base price
		for (uint8 i = 0; i < MAX_EXTENDED_COST_ITEMS; ++i) {
			if (iece->RequiredItem[i]
					&& !HasItemCount(iece->RequiredItem[i],
							(iece->RequiredItemCount[i] * count))) {
				SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL);
				return false;
			}
		}

		// currency price
		for (uint8 i = 0; i < MAX_EXTENDED_COST_CURRENCIES; ++i) {
			switch (iece->RequiredCurrency[i]) {
			case NULL:
				break;
			case CURRENCY_TYPE_CONQUEST_POINTS: // There are currencies that include multiplier in dbc
			case CURRENCY_TYPE_HONOR_POINTS:
			case CURRENCY_TYPE_JUSTICE_POINTS:
			case CURRENCY_TYPE_VALOR_POINTS:
				if (!HasCurrency(iece->RequiredCurrency[i],
						iece->RequiredCurrencyCount[i])) {
					SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL,
							NULL);
					return false;
				}
				break;
			default: // other ones need multiplier
				if (!HasCurrency(
						iece->RequiredCurrency[i],
						iece->RequiredCurrencyCount[i]
								* PLAYER_CURRENCY_PRECISION)) {
					SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL,
							NULL);
					return false;
				}
			}
		}

		// check for personal arena rating requirement
		if (GetMaxPersonalArenaRatingRequirement(iece->RequiredArenaSlot)
				< iece->RequiredPersonalArenaRating) {
			// probably not the proper equip err
			SendEquipError(EQUIP_ERR_CANT_EQUIP_RANK, NULL, NULL);
			return false;
		}
	}

	uint32 price =
			crItem->IsGoldRequired(pProto) ? pProto->BuyPrice * count : 0;

	// reputation discount
	if (price)
		price = uint32(floor(price * GetReputationPriceDiscount(pCreature)));

	if (!HasEnoughMoney(price)) {
		SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, item, 0);
		return false;
	}

	if ((bag == NULL_BAG && slot == NULL_SLOT) || IsInventoryPos(bag, slot)) {
		if (!_StoreOrEquipNewItem(vendorslot, item, count, bag, slot, price,
				pProto, pCreature, crItem, true))
			return false;
	} else if (IsEquipmentPos(bag, slot)) {
		if (pProto->BuyCount * count != 1) {
			SendEquipError(EQUIP_ERR_ITEM_CANT_BE_EQUIPPED, NULL, NULL);
			return false;
		}
		if (!_StoreOrEquipNewItem(vendorslot, item, count, bag, slot, price,
				pProto, pCreature, crItem, false))
			return false;
	} else {
		SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL);
		return false;
	}

	return crItem->maxcount != 0;
}

uint32 Player::GetMaxPersonalArenaRatingRequirement(uint32 minarenaslot) {
	// returns the maximal personal arena rating that can be used to purchase items requiring this condition
	// the personal rating of the arena team must match the required limit as well
	// so return max[in arenateams](min(personalrating[teamtype], teamrating[teamtype]))
	uint32 max_personal_rating = 0;
	for (uint8 i = minarenaslot; i < MAX_ARENA_SLOT; ++i) {
		if (ArenaTeam * at = sObjectMgr->GetArenaTeamById(GetArenaTeamId(i))) {
			uint32 p_rating = GetArenaPersonalRating(i);
			uint32 t_rating = at->GetRating();
			p_rating = p_rating < t_rating ? p_rating : t_rating;
			if (max_personal_rating < p_rating)
				max_personal_rating = p_rating;
		}
	}
	return max_personal_rating;
}

void Player::UpdateHomebindTime(uint32 time) {
	// GMs never get homebind timer online
	if (m_InstanceValid || isGameMaster()) {
		if (m_HomebindTimer) // instance valid, but timer not reset
		{
			// hide reminder
			WorldPacket data(SMSG_RAID_GROUP_ONLY, 4 + 4);
			data << uint32(0);
			data << uint32(0);
			GetSession()->SendPacket(&data);
		}
		// instance is valid, reset homebind timer
		m_HomebindTimer = 0;
	} else if (m_HomebindTimer > 0) {
		if (time >= m_HomebindTimer) {
			// teleport to nearest graveyard
			RepopAtGraveyard();
		} else
			m_HomebindTimer -= time;
	} else {
		// instance is invalid, start homebind timer
		m_HomebindTimer = 60000;
		// send message to player
		WorldPacket data(SMSG_RAID_GROUP_ONLY, 4 + 4);
		data << uint32(m_HomebindTimer);
		data << uint32(1);
		GetSession()->SendPacket(&data);
		sLog->outDebug(
				LOG_FILTER_MAPS,
				"PLAYER: Player '%s' (GUID: %u) will be teleported to homebind in 60 seconds",
				GetName(), GetGUIDLow());
	}
}

void Player::UpdatePvPState(bool onlyFFA) {
	// TODO: should we always synchronize UNIT_FIELD_BYTES_2, 1 of controller and controlled?
	if (!pvpInfo.inNoPvPArea && !isGameMaster()
			&& (pvpInfo.inFFAPvPArea || sWorld->IsFFAPvPRealm())) {
		if (!HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP)) {
			SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
			for (ControlList::iterator itr = m_Controlled.begin();
					itr != m_Controlled.end(); ++itr)
				(*itr)->SetByteValue(UNIT_FIELD_BYTES_2, 1,
						UNIT_BYTE2_FLAG_FFA_PVP);
		}
	} else if (HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP)) {
		RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
		for (ControlList::iterator itr = m_Controlled.begin();
				itr != m_Controlled.end(); ++itr)
			(*itr)->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1,
					UNIT_BYTE2_FLAG_FFA_PVP);
	}

	if (onlyFFA)
		return;

	if (pvpInfo.inHostileArea) // in hostile area
	{
		if (!IsPvP() || pvpInfo.endTimer != 0)
			UpdatePvP(true, true);
	} else // in friendly area
	{
		if (IsPvP() && !HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP)
				&& pvpInfo.endTimer == 0)
			pvpInfo.endTimer = time(0); // start toggle-off
	}
}

void Player::UpdatePvP(bool state, bool override) {
	if (!state || override) {
		SetPvP(state);
		pvpInfo.endTimer = 0;
	} else {
		pvpInfo.endTimer = time(NULL);
		SetPvP(state);
	}
}

void Player::AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo,
		uint32 itemId, Spell* spell, bool infinityCooldown) {
	// init cooldown values
	uint32 cat = 0;
	int32 rec = -1;
	int32 catrec = -1;

	// some special item spells without correct cooldown in SpellInfo
	// cooldown information stored in item prototype
	// This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client.

	if (itemId) {
		if (ItemPrototype const* proto = ObjectMgr::GetItemPrototype(itemId)) {
			for (uint8 idx = 0; idx < MAX_ITEM_SPELLS; ++idx) {
				if (uint32(proto->Spells[idx].SpellId) == spellInfo->Id) {
					cat = proto->Spells[idx].SpellCategory;
					rec = proto->Spells[idx].SpellCooldown;
					catrec = proto->Spells[idx].SpellCategoryCooldown;
					break;
				}
			}
		}
	}

	// if no cooldown found above then base at DBC data
	if (rec < 0 && catrec < 0) {
		cat = spellInfo->Category;
		rec = spellInfo->RecoveryTime;
		catrec = spellInfo->CategoryRecoveryTime;
	}

	time_t curTime = time(NULL);

	time_t catrecTime;
	time_t recTime;

	// overwrite time for selected category
	if (infinityCooldown) {
		// use +MONTH as infinity mark for spell cooldown (will checked as MONTH/2 at save ans skipped)
		// but not allow ignore until reset or re-login
		catrecTime = catrec > 0 ? curTime + infinityCooldownDelay : 0;
		recTime = rec > 0 ? curTime + infinityCooldownDelay : catrecTime;
	} else {
		// shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK)
		// prevent 0 cooldowns set by another way
		if (rec <= 0 && catrec <= 0
				&& (cat == 76
						|| (IsAutoRepeatRangedSpell(spellInfo)
								&& spellInfo->Id != 75)))
			rec = GetAttackTime(RANGED_ATTACK);

		// Now we have cooldown data (if found any), time to apply mods
		if (rec > 0)
			ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, rec, spell);

		if (catrec > 0)
			ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, catrec, spell);

		// replace negative cooldowns by 0
		if (rec < 0)
			rec = 0;
		if (catrec < 0)
			catrec = 0;

		// no cooldown after applying spell mods
		if (rec == 0 && catrec == 0)
			return;

		catrecTime = catrec ? curTime + catrec / IN_MILLISECONDS : 0;
		recTime = rec ? curTime + rec / IN_MILLISECONDS : catrecTime;
	}

	// self spell cooldown
	if (recTime > 0)
		AddSpellCooldown(spellInfo->Id, itemId, recTime);

	// category spells
	if (cat && catrec > 0) {
		SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(
				cat);
		if (i_scstore != sSpellCategoryStore.end()) {
			for (SpellCategorySet::const_iterator i_scset =
					i_scstore->second.begin();
					i_scset != i_scstore->second.end(); ++i_scset) {
				if (*i_scset == spellInfo->Id) // skip main spell, already handled above
					continue;

				AddSpellCooldown(*i_scset, itemId, catrecTime);
			}
		}
	}
}

void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time) {
	SpellCooldown sc;
	sc.end = end_time;
	sc.itemid = itemid;
	m_spellCooldowns[spellid] = sc;
}

void Player::SendCooldownEvent(SpellEntry const *spellInfo, uint32 itemId,
		Spell* spell) {
	// start cooldowns at server side, if any
	AddSpellAndCategoryCooldowns(spellInfo, itemId, spell);

	// Send activate cooldown timer (possible 0) at client side
	WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8);
	data << uint32(spellInfo->Id);
	data << uint64(GetGUID());
	SendDirectMessage(&data);
}

void Player::UpdatePotionCooldown(Spell* spell) {
	// no potion used i combat or still in combat
	if (!m_lastPotionId || isInCombat())
		return;

	// Call not from spell cast, send cooldown event for item spells if no in combat
	if (!spell) {
		// spell/item pair let set proper cooldown (except not existed charged spell cooldown spellmods for potions)
		if (ItemPrototype const* proto = ObjectMgr::GetItemPrototype(m_lastPotionId))
			for (uint8 idx = 0; idx < MAX_ITEM_SPELLS; ++idx)
				if (proto->Spells[idx].SpellId
						&& proto->Spells[idx].SpellTrigger
								== ITEM_SPELLTRIGGER_ON_USE)
					if (SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[idx].SpellId))
						SendCooldownEvent(spellInfo, m_lastPotionId);
	}
	// from spell cases (m_lastPotionId set in Spell::SendSpellCooldown)
	else
		SendCooldownEvent(spell->m_spellInfo, m_lastPotionId, spell);

	m_lastPotionId = 0;
}

//slot to be excluded while counting
bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition,
		int8 slot) {
	if (!enchantmentcondition)
		return true;

	SpellItemEnchantmentConditionEntry const *Condition =
			sSpellItemEnchantmentConditionStore.LookupEntry(
					enchantmentcondition);

	if (!Condition)
		return true;

	uint8 curcount[4] = { 0, 0, 0, 0 };

	//counting current equipped gem colors
	for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) {
		if (i == slot)
			continue;
		Item *pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
		if (pItem2 && !pItem2->IsBroken()
				&& pItem2->GetProto()->Socket[0].Color) {
			for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT;
					enchant_slot < SOCK_ENCHANTMENT_SLOT + 3; ++enchant_slot) {
				uint32 enchant_id = pItem2->GetEnchantmentId(
						EnchantmentSlot(enchant_slot));
				if (!enchant_id)
					continue;

				SpellItemEnchantmentEntry const* enchantEntry =
						sSpellItemEnchantmentStore.LookupEntry(enchant_id);
				if (!enchantEntry)
					continue;

				uint32 gemid = enchantEntry->GemID;
				if (!gemid)
					continue;

				ItemPrototype const* gemProto = sItemStorage.LookupEntry<
						ItemPrototype>(gemid);
				if (!gemProto)
					continue;

				GemPropertiesEntry const* gemProperty =
						sGemPropertiesStore.LookupEntry(
								gemProto->GemProperties);
				if (!gemProperty)
					continue;

				uint8 GemColor = gemProperty->color;

				for (uint8 b = 0, tmpcolormask = 1; b < 4; b++, tmpcolormask <<=
						1) {
					if (tmpcolormask & GemColor)
						++curcount[b];
				}
			}
		}
	}

	bool activate = true;

	for (uint8 i = 0; i < 5; i++) {
		if (!Condition->Color[i])
			continue;

		uint32 _cur_gem = curcount[Condition->Color[i] - 1];

		// if have <CompareColor> use them as count, else use <value> from Condition
		uint32 _cmp_gem =
				Condition->CompareColor[i] ?
						curcount[Condition->CompareColor[i] - 1] :
						Condition->Value[i];

		switch (Condition->Comparator[i]) {
		case 2: // requires less <color> than (<value> || <comparecolor>) gems
			activate &= (_cur_gem < _cmp_gem) ? true : false;
			break;
		case 3: // requires more <color> than (<value> || <comparecolor>) gems
			activate &= (_cur_gem > _cmp_gem) ? true : false;
			break;
		case 5: // requires at least <color> than (<value> || <comparecolor>) gems
			activate &= (_cur_gem >= _cmp_gem) ? true : false;
			break;
		}
	}

	sLog->outDebug(
			LOG_FILTER_PLAYER_ITEMS,
			"Checking Condition %u, there are %u Meta Gems, %u Red Gems, %u Yellow Gems and %u Blue Gems, Activate:%s",
			enchantmentcondition, curcount[0], curcount[1], curcount[2],
			curcount[3], activate ? "yes" : "no");

	return activate;
}

void Player::CorrectMetaGemEnchants(uint8 exceptslot, bool apply) {
	//cycle all equipped items
	for (uint32 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END;
			++slot) {
		//enchants for the slot being socketed are handled by Player::ApplyItemMods
		if (slot == exceptslot)
			continue;

		Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, slot);

		if (!pItem || !pItem->GetProto()->Socket[0].Color)
			continue;

		for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT;
				enchant_slot < SOCK_ENCHANTMENT_SLOT + 3; ++enchant_slot) {
			uint32 enchant_id = pItem->GetEnchantmentId(
					EnchantmentSlot(enchant_slot));
			if (!enchant_id)
				continue;

			SpellItemEnchantmentEntry const* enchantEntry =
					sSpellItemEnchantmentStore.LookupEntry(enchant_id);
			if (!enchantEntry)
				continue;

			uint32 condition = enchantEntry->EnchantmentCondition;
			if (condition) {
				//was enchant active with/without item?
				bool wasactive = EnchantmentFitsRequirements(condition,
						apply ? exceptslot : -1);
				//should it now be?
				if (wasactive
						^ EnchantmentFitsRequirements(condition,
								apply ? -1 : exceptslot)) {
					// ignore item gem conditions
					//if state changed, (dis)apply enchant
					ApplyEnchantment(pItem, EnchantmentSlot(enchant_slot),
							!wasactive, true, true);
				}
			}
		}
	}
}

//if false -> then toggled off if was on| if true -> toggled on if was off AND meets requirements
void Player::ToggleMetaGemsActive(uint8 exceptslot, bool apply) {
	//cycle all equipped items
	for (int slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) {
		//enchants for the slot being socketed are handled by WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
		if (slot == exceptslot)
			continue;

		Item *pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, slot);

		if (!pItem || !pItem->GetProto()->Socket[0].Color) //if item has no sockets or no item is equipped go to next item
			continue;

		//cycle all (gem)enchants
		for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT;
				enchant_slot < SOCK_ENCHANTMENT_SLOT + 3; ++enchant_slot) {
			uint32 enchant_id = pItem->GetEnchantmentId(
					EnchantmentSlot(enchant_slot));
			if (!enchant_id) //if no enchant go to next enchant(slot)
				continue;

			SpellItemEnchantmentEntry const* enchantEntry =
					sSpellItemEnchantmentStore.LookupEntry(enchant_id);
			if (!enchantEntry)
				continue;

			//only metagems to be (de)activated, so only enchants with condition
			uint32 condition = enchantEntry->EnchantmentCondition;
			if (condition)
				ApplyEnchantment(pItem, EnchantmentSlot(enchant_slot), apply);
		}
	}
}

void Player::SetBattlegroundEntryPoint() {
	// Taxi path store
	if (!m_taxi.empty()) {
		m_bgData.mountSpell = 0;
		m_bgData.taxiPath[0] = m_taxi.GetTaxiSource();
		m_bgData.taxiPath[1] = m_taxi.GetTaxiDestination();

		// On taxi we don't need check for dungeon
		m_bgData.joinPos = WorldLocation(GetMapId(), GetPositionX(),
				GetPositionY(), GetPositionZ(), GetOrientation());
		return;
	} else {
		m_bgData.ClearTaxiPath();

		// Mount spell id storing
		if (IsMounted()) {
			AuraEffectList const& auras = GetAuraEffectsByType(
					SPELL_AURA_MOUNTED);
			if (!auras.empty())
				m_bgData.mountSpell = (*auras.begin())->GetId();
		} else
			m_bgData.mountSpell = 0;

		// If map is dungeon find linked graveyard
		if (GetMap()->IsDungeon()) {
			if (const WorldSafeLocsEntry* entry = sObjectMgr->GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam())) {
				m_bgData.joinPos = WorldLocation(entry->map_id, entry->x,
						entry->y, entry->z, 0.0f);
				return;
			} else
				sLog->outError(
						"SetBattlegroundEntryPoint: Dungeon map %u has no linked graveyard, setting home location as entry point.",
						GetMapId());
		}
		// If new entry point is not BG or arena set it
		else if (!GetMap()->IsBattlegroundOrArena()) {
			m_bgData.joinPos = WorldLocation(GetMapId(), GetPositionX(),
					GetPositionY(), GetPositionZ(), GetOrientation());
			return;
		}
	}

	// In error cases use homebind position
	m_bgData.joinPos = WorldLocation(m_homebindMapId, m_homebindX, m_homebindY,
			m_homebindZ, 0.0f);
}

void Player::LeaveBattleground(bool teleportToEntryPoint) {
	if (Battleground *bg = GetBattleground()) {
		bg->RemovePlayerAtLeave(GetGUID(), teleportToEntryPoint, true);

		// call after remove to be sure that player resurrected for correct cast
		if (bg->isBattleground() && !isGameMaster()
				&& sWorld->getBoolConfig(CONFIG_BATTLEGROUND_CAST_DESERTER)) {
			if (bg->GetStatus() == STATUS_IN_PROGRESS
					|| bg->GetStatus() == STATUS_WAIT_JOIN) {
				//lets check if player was teleported from BG and schedule delayed Deserter spell cast
				if (IsBeingTeleportedFar()) {
					ScheduleDelayedOperation(DELAYED_SPELL_CAST_DESERTER);
					return;
				}

				CastSpell(this, 26013, true); // Deserter
			}
		}
	}
}

bool Player::CanJoinToBattleground() const {
	// check Deserter debuff
	if (HasAura(26013))
		return false;

	return true;
}

bool Player::CanReportAfkDueToLimit() {
	// a player can complain about 15 people per 5 minutes
	if (m_bgData.bgAfkReportedCount++ >= 15)
		return false;

	return true;
}

///This player has been blamed to be inactive in a battleground
void Player::ReportedAfkBy(Player* reporter) {
	Battleground *bg = GetBattleground();
	if (!bg || bg != reporter->GetBattleground()
			|| GetTeam() != reporter->GetTeam())
		return;

	// check if player has 'Idle' or 'Inactive' debuff
	if (m_bgData.bgAfkReporter.find(reporter->GetGUIDLow())
			== m_bgData.bgAfkReporter.end() && !HasAura(43680)
			&& !HasAura(43681) && reporter->CanReportAfkDueToLimit()) {
		m_bgData.bgAfkReporter.insert(reporter->GetGUIDLow());
		// 3 players have to complain to apply debuff
		if (m_bgData.bgAfkReporter.size() >= 3) {
			// cast 'Idle' spell
			CastSpell(this, 43680, true);
			m_bgData.bgAfkReporter.clear();
		}
	}
}

WorldLocation Player::GetStartPosition() const {
	PlayerInfo const *info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
	uint32 mapId = info->mapId;
	if (getClass() == CLASS_DEATH_KNIGHT && HasSpell(50977))
		mapId = 0;
	return WorldLocation(mapId, info->positionX, info->positionY,
			info->positionZ, 0);
}

bool Player::isValid() const {
	if (!Unit::isValid())
		return false;

	if (GetSession()->PlayerLogout() || GetSession()->PlayerLoading())
		return false;

	return true;
}

bool Player::canSeeAlways(WorldObject const* obj) const {
	if (Unit::canSeeAlways(obj))
		return true;

	// Always can see self
	if (m_mover == obj)
		return true;

	if (uint64 guid = GetUInt64Value(PLAYER_FARSIGHT))
		if (obj->GetGUID() == guid)
			return true;

	return false;
}

bool Player::isAlwaysDetectableFor(WorldObject const* seer) const {
	if (Unit::isAlwaysDetectableFor(seer))
		return true;

	if (const Player* seerPlayer = seer->ToPlayer())
		if (IsGroupVisibleFor(seerPlayer))
			return true;

	return false;
}

bool Player::IsVisibleGloballyFor(Player* u) const {
	if (!u)
		return false;

	// Always can see self
	if (u == this)
		return true;

	// Visible units, always are visible for all players
	if (IsVisible())
		return true;

	// GMs are visible for higher gms (or players are visible for gms)
	if (u->GetSession()->GetSecurity() > SEC_PLAYER)
		return GetSession()->GetSecurity() <= u->GetSession()->GetSecurity();

	// non faction visibility non-breakable for non-GMs
	if (!IsVisible())
		return false;

	// non-gm stealth/invisibility not hide from global player lists
	return true;
}

template<class T>
inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, T* target,
		std::set<Unit*>& /*v*/) {
	s64.insert(target->GetGUID());
}

template<>
inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, GameObject* target,
		std::set<Unit*>& /*v*/) {
	if (!target->IsTransport())
		s64.insert(target->GetGUID());
}

template<>
inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, Creature* target,
		std::set<Unit*>& v) {
	s64.insert(target->GetGUID());
	v.insert(target);
}

template<>
inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, Player* target,
		std::set<Unit*>& v) {
	s64.insert(target->GetGUID());
	v.insert(target);
}

template<class T>
inline void BeforeVisibilityDestroy(T* /*t*/, Player* /*p*/) {
}

template<>
inline void BeforeVisibilityDestroy<Creature>(Creature* t, Player* p) {
	if (p->GetPetGUID() == t->GetGUID() && t->ToCreature()->isPet())
		((Pet*) t)->Remove(PET_SLOT_ACTUAL_PET_SLOT, true);
}

void Player::UpdateVisibilityOf(WorldObject* target) {
	if (HaveAtClient(target)) {
		if (!canSeeOrDetect(target, false, true)) {
			if (target->GetTypeId() == TYPEID_UNIT)
				BeforeVisibilityDestroy<Creature>(target->ToCreature(), this);

			target->DestroyForPlayer(this);
			m_clientGUIDs.erase(target->GetGUID());

#ifdef ARKCORE_DEBUG
			sLog->outDebug(LOG_FILTER_MAPS, "Object %u (Type: %u) out of range for player %u. Distance = %f", target->GetGUIDLow(), target->GetTypeId(), GetGUIDLow(), GetDistance(target));
#endif
		}
	} else {
		if (canSeeOrDetect(target, false, true)) {
			//if (target->isType(TYPEMASK_UNIT) && ((Unit*)target)->m_Vehicle)
			//    UpdateVisibilityOf(((Unit*)target)->m_Vehicle);

			target->SendUpdateToPlayer(this);
			m_clientGUIDs.insert(target->GetGUID());

#ifdef ARKCORE_DEBUG
			sLog->outDebug(LOG_FILTER_MAPS, "Object %u (Type: %u) is visible now for player %u. Distance = %f", target->GetGUIDLow(), target->GetTypeId(), GetGUIDLow(), GetDistance(target));
#endif

			// target aura duration for caster show only if target exist at caster client
			// send data at target visibility change (adding to client)
			if (target->isType(TYPEMASK_UNIT))
				SendInitialVisiblePackets((Unit*) target);
		}
	}
}

void Player::UpdateTriggerVisibility() {
	if (m_clientGUIDs.empty())
		return;

	UpdateData udata;
	udata.m_map = uint16(GetMapId());
	WorldPacket packet;
	for (ClientGUIDs::iterator itr = m_clientGUIDs.begin();
			itr != m_clientGUIDs.end(); ++itr) {
		if (IS_CREATURE_GUID(*itr)) {
			Creature *obj = IsInWorld() ? GetMap()->GetCreature(*itr) : NULL;
			if (!obj || !obj->isTrigger())
				continue;

			obj->BuildCreateUpdateBlockForPlayer(&udata, this);
		}
	}
	udata.BuildPacket(&packet);
	GetSession()->SendPacket(&packet);
}

void Player::SendInitialVisiblePackets(Unit* target) {
	SendAurasForTarget(target);
	if (target->isAlive()) {
		if (target->GetMotionMaster()->GetCurrentMovementGeneratorType()
				!= IDLE_MOTION_TYPE)
			target->SendMonsterMoveWithSpeedToCurrentDestination(this);
		if (target->HasUnitState(UNIT_STAT_MELEE_ATTACKING)
				&& target->getVictim())
			target->SendMeleeAttackStart(target->getVictim());
	}
}

template<class T>
void Player::UpdateVisibilityOf(T* target, UpdateData& data,
		std::set<Unit*>& visibleNow) {
	if (HaveAtClient(target)) {
		if (!canSeeOrDetect(target, false, true)) {
			BeforeVisibilityDestroy<T>(target, this);

			target->BuildOutOfRangeUpdateBlock(&data);
			m_clientGUIDs.erase(target->GetGUID());

#ifdef ARKCORE_DEBUG
			sLog->outDebug(LOG_FILTER_MAPS, "Object %u (Type: %u, Entry: %u) is out of range for player %u. Distance = %f", target->GetGUIDLow(), target->GetTypeId(), target->GetEntry(), GetGUIDLow(), GetDistance(target));
#endif
		}
	} else //if (visibleNow.size() < 30 || target->GetTypeId() == TYPEID_UNIT && target->ToCreature()->IsVehicle())
	{
		if (canSeeOrDetect(target, false, true)) {
			//if (target->isType(TYPEMASK_UNIT) && ((Unit*)target)->m_Vehicle)
			//    UpdateVisibilityOf(((Unit*)target)->m_Vehicle, data, visibleNow);

			target->BuildCreateUpdateBlockForPlayer(&data, this);
			UpdateVisibilityOf_helper(m_clientGUIDs, target, visibleNow);

#ifdef ARKCORE_DEBUG
			sLog->outDebug(LOG_FILTER_MAPS, "Object %u (Type: %u, Entry: %u) is visible now for player %u. Distance = %f", target->GetGUIDLow(), target->GetTypeId(), target->GetEntry(), GetGUIDLow(), GetDistance(target));
#endif
		}
	}
}

template void Player::UpdateVisibilityOf(Player* target, UpdateData& data,
		std::set<Unit*>& visibleNow);
template void Player::UpdateVisibilityOf(Creature* target, UpdateData& data,
		std::set<Unit*>& visibleNow);
template void Player::UpdateVisibilityOf(Corpse* target, UpdateData& data,
		std::set<Unit*>& visibleNow);
template void Player::UpdateVisibilityOf(GameObject* target, UpdateData& data,
		std::set<Unit*>& visibleNow);
template void Player::UpdateVisibilityOf(DynamicObject* target,
		UpdateData& data, std::set<Unit*>& visibleNow);

void Player::UpdateObjectVisibility(bool forced) {
	if (!forced)
		AddToNotify(NOTIFY_VISIBILITY_CHANGED);
	else {
		Unit::UpdateObjectVisibility(true);
		UpdateVisibilityForPlayer();
	}
}

void Player::UpdateVisibilityForPlayer() {
	// updates visibility of all objects around point of view for current player
	Trinity::VisibleNotifier notifier(*this);
	m_seer->VisitNearbyObject(GetSightRange(), notifier);
	notifier.SendToSelf(); // send gathered data
}

void Player::InitPrimaryProfessions() {
	SetFreePrimaryProfessions(
			sWorld->getIntConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL));
}

void Player::ModifyMoney(int32 d) {
	sScriptMgr->OnPlayerMoneyChanged(this, d);

	if (d < 0)
		SetMoney(GetMoney() > uint64(-d) ? GetMoney() + d : 0);

	else {
		uint64 newAmount = 0;
		if (GetMoney() < uint32(MAX_MONEY_AMOUNT - d)) {
			newAmount = GetMoney() + d;

			SetGuildMoneyModifier(1);

			if (Guild * pGuild = sObjectMgr->GetGuildById(GetGuildId())) {
				if (pGuild) {
					if (!this->HasAura(83940) && !this->HasAura(83941))
						SetGuildMoneyModifier(10);

					if (this->HasAura(83940) && !this->HasAura(83941))
						SetGuildMoneyModifier(15);

					if (this->HasAura(83941) && this->HasAura(83940))
						SetGuildMoneyModifier(25);

					//If we withdraw money from guild we don't get lootGUID, same in quests ;)
					if (this->GetLootGUID()) {
						uint64 GuildMoney = (d
								* (GetGuildMoneyModifier() * 0.01));

						if (GuildMoney < 1)
							GuildMoney = 1;

						pGuild->SetGuildMoney(GuildMoney);
					}
				}
			}
			this->SendPlayerMoneyNotify(this, d, GetGuildMoneyModifier());
		} else {
			// "At Gold Limit"
			newAmount = MAX_MONEY_AMOUNT;
			if (d)
				SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, NULL, NULL);
		}
		SetMoney(newAmount);
	}
}

Unit * Player::GetSelectedUnit() const {
	if (m_curSelection)
		return ObjectAccessor::GetUnit(*this, m_curSelection);
	return NULL;
}

Player * Player::GetSelectedPlayer() const {
	if (m_curSelection)
		return ObjectAccessor::GetPlayer(*this, m_curSelection);
	return NULL;
}

void Player::SendComboPoints() {
	Unit *combotarget = ObjectAccessor::GetUnit(*this, m_comboTarget);
	if (combotarget) {
		WorldPacket data;
		if (m_mover != this) {
			data.Initialize(
					SMSG_PET_UPDATE_COMBO_POINTS,
					m_mover->GetPackGUID().size()
							+ combotarget->GetPackGUID().size() + 1);
			data.append(m_mover->GetPackGUID());
		} else
			data.Initialize(SMSG_UPDATE_COMBO_POINTS,
					combotarget->GetPackGUID().size() + 1);
		data.append(combotarget->GetPackGUID());
		data << uint8(m_comboPoints);
		GetSession()->SendPacket(&data);
	}
}

void Player::AddComboPoints(Unit* target, int8 count, Spell * spell) {
	if (!count)
		return;

	int8 * comboPoints = spell ? &spell->m_comboPointGain : &m_comboPoints;

	// without combo points lost (duration checked in aura)
	RemoveAurasByType(SPELL_AURA_RETAIN_COMBO_POINTS);

	if (target->GetGUID() == m_comboTarget)
		*comboPoints += count;
	else {
		if (m_comboTarget)
			if (Unit* target2 = ObjectAccessor::GetUnit(*this, m_comboTarget))
				target2->RemoveComboPointHolder(GetGUIDLow());

		// Spells will always add value to m_comboPoints eventualy, so it must be cleared first
		if (spell)
			m_comboPoints = 0;

		m_comboTarget = target->GetGUID();
		*comboPoints = count;

		target->AddComboPointHolder(GetGUIDLow());
	}

	if (*comboPoints > 5)
		*comboPoints = 5;
	else if (*comboPoints < 0)
		*comboPoints = 0;

	if (!spell)
		SendComboPoints();
}

void Player::GainSpellComboPoints(int8 count) {
	if (!count)
		return;

	m_comboPoints += count;
	if (m_comboPoints > 5)
		m_comboPoints = 5;
	else if (m_comboPoints < 0)
		m_comboPoints = 0;

	SendComboPoints();
}

void Player::ClearComboPoints() {
	if (!m_comboTarget)
		return;

	// without combopoints lost (duration checked in aura)
	RemoveAurasByType(SPELL_AURA_RETAIN_COMBO_POINTS);

	m_comboPoints = 0;

	SendComboPoints();

	if (Unit* target = ObjectAccessor::GetUnit(*this, m_comboTarget))
		target->RemoveComboPointHolder(GetGUIDLow());

	m_comboTarget = 0;
}

void Player::SetGroup(Group *group, int8 subgroup) {
	if (group == NULL)
		m_group.unlink();
	else {
		// never use SetGroup without a subgroup unless you specify NULL for group
		ASSERT(subgroup >= 0);
		m_group.link(group, this);
		m_group.setSubGroup((uint8) subgroup);
	}

	UpdateObjectVisibility(false);
}

void Player::SendInitialPacketsBeforeAddToMap() {
	GetSocial()->SendSocialList(this,
			SOCIAL_FLAG_FRIEND | SOCIAL_FLAG_IGNORED | SOCIAL_FLAG_MUTED);

	// Homebind
	WorldPacket data(SMSG_BINDPOINTUPDATE, 5 * 4);
	data << m_homebindX << m_homebindY << m_homebindZ;
	data << (uint32) m_homebindMapId;
	data << (uint32) m_homebindAreaId;
	GetSession()->SendPacket(&data);

	// SMSG_SET_PROFICIENCY
	// SMSG_SET_PCT_SPELL_MODIFIER
	// SMSG_SET_FLAT_SPELL_MODIFIER
	// SMSG_UPDATE_AURA_DURATION

	SendTalentsInfoData(false);

	// SMSG_INSTANCE_DIFFICULTY
	data.Initialize(SMSG_INSTANCE_DIFFICULTY, 4 + 4);
	data << uint32(GetMap()->GetDifficulty());
	data << uint32(0);
	GetSession()->SendPacket(&data);

	SendInitialSpells();

	data.Initialize(SMSG_SEND_UNLEARN_SPELLS, 4);
	data << uint32(0); // count, for (count) uint32;
	GetSession()->SendPacket(&data);

	SendInitialActionButtons();
	m_reputationMgr.SendInitialReputations();
	m_achievementMgr.SendAllAchievementData(); //marker1

	SendCurrencies();
	SendEquipmentSetList();

	data.Initialize(SMSG_LOGIN_SETTIMESPEED, 4 + 4 + 4);
	data << uint32(secsToTimeBitFields(sWorld->GetGameTime()));
	data << (float) 0.01666667f; // game speed
	data << uint32(0); // added in 3.1.2
	GetSession()->SendPacket(&data);

	GetReputationMgr().SendForceReactions(); // SMSG_SET_FORCED_REACTIONS

	// SMSG_TALENTS_INFO x 2 for pet (unspent points and talents in separate packets...)
	// SMSG_PET_GUIDS
	// SMSG_UPDATE_WORLD_STATE
	// SMSG_POWER_UPDATE
}

void Player::SendInitialPacketsAfterAddToMap() {
	UpdateVisibilityForPlayer();

	// update zone
	uint32 newzone, newarea;
	GetZoneAndAreaId(newzone, newarea);
	SendInitWorldStates(newzone, newarea);
	UpdateZone(newzone, newarea);

	ResetTimeSync();
	SendTimeSync();

	CastSpell(this, 836, true); // LOGINEFFECT

	// set some aura effects that send packet to player client after add player to map
	// SendMessageToSet not send it to player not it map, only for aura that not changed anything at re-apply
	// same auras state lost at far teleport, send it one more time in this case also
	static const AuraType auratypes[] = { SPELL_AURA_MOD_FEAR,
			SPELL_AURA_TRANSFORM, SPELL_AURA_WATER_WALK,
			SPELL_AURA_FEATHER_FALL, SPELL_AURA_HOVER, SPELL_AURA_SAFE_FALL,
			SPELL_AURA_FLY, SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED,
			SPELL_AURA_NONE };
	for (AuraType const* itr = &auratypes[0]; itr && itr[0] != SPELL_AURA_NONE;
			++itr) {
		Unit::AuraEffectList const& auraList = GetAuraEffectsByType(*itr);
		if (!auraList.empty())
			auraList.front()->HandleEffect(this,
					AURA_EFFECT_HANDLE_SEND_FOR_CLIENT, true);
	}

	if (HasAuraType(SPELL_AURA_MOD_STUN))
		SetMovement(MOVE_ROOT);

	// manual send package (have code in HandleEffect(this, AURA_EFFECT_HANDLE_SEND_FOR_CLIENT, true); that don't must be re-applied.
	if (HasAuraType(SPELL_AURA_MOD_ROOT)) {
		WorldPacket data2(SMSG_FORCE_MOVE_ROOT, 10);
		data2.append(GetPackGUID());
		data2 << (uint32) 2;
		//SendMessageToSet(&data2, true);
		m_session->SendPacket(&data2);
	}

	SendAurasForTarget(this);
	SendEnchantmentDurations(); // must be after add to map
	SendItemDurations(); // must be after add to map

	// raid downscaling - send difficulty to player
	if (GetMap()->IsRaid()) {
		if (GetMap()->GetDifficulty() != GetRaidDifficulty()) {
			StoreRaidMapDifficulty();
			SendRaidDifficulty(GetGroup() != NULL, GetStoredRaidDifficulty());
		}
	} else if (GetRaidDifficulty() != GetStoredRaidDifficulty())
		SendRaidDifficulty(GetGroup() != NULL);
}

void Player::SendUpdateToOutOfRangeGroupMembers() {
	if (m_groupUpdateMask == GROUP_UPDATE_FLAG_NONE)
		return;
	if (Group* group = GetGroup())
		group->UpdatePlayerOutOfRange(this);

	m_groupUpdateMask = GROUP_UPDATE_FLAG_NONE;
	m_auraRaidUpdateMask = 0;
	if (Pet *pet = GetPet())
		pet->ResetAuraUpdateMaskForRaid();
}

void Player::SendTransferAborted(uint32 mapid, TransferAbortReason reason,
		uint8 arg) {
	WorldPacket data(SMSG_TRANSFER_ABORTED, 4 + 2);
	data << uint32(mapid);
	data << uint8(reason); // transfer abort reason
	switch (reason) {
	case TRANSFER_ABORT_INSUF_EXPAN_LVL:
	case TRANSFER_ABORT_DIFFICULTY:
	case TRANSFER_ABORT_UNIQUE_MESSAGE:
		// these are the ONLY cases that have an extra argument in the packet!!!
		data << uint8(arg);
		break;
	default:
		break;
	}
	GetSession()->SendPacket(&data);
}

void Player::SendInstanceResetWarning(uint32 mapid, Difficulty difficulty,
		uint32 time) {
	// type of warning, based on the time remaining until reset
	uint32 type;
	if (time > 3600)
		type = RAID_INSTANCE_WELCOME;
	else if (time > 900 && time <= 3600)
		type = RAID_INSTANCE_WARNING_HOURS;
	else if (time > 300 && time <= 900)
		type = RAID_INSTANCE_WARNING_MIN;
	else
		type = RAID_INSTANCE_WARNING_MIN_SOON;

	WorldPacket data(SMSG_RAID_INSTANCE_MESSAGE, 4 + 4 + 4 + 4);
	data << uint32(type);
	data << uint32(mapid);
	data << uint32(difficulty); // difficulty
	data << uint32(time);
	if (type == RAID_INSTANCE_WELCOME) {
		data << uint8(0); // is your (1)
		data << uint8(0); // is extended (1), ignored if prev field is 0
	}
	GetSession()->SendPacket(&data);
}

void Player::ApplyEquipCooldown(Item * pItem) {
	if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_PROTO_FLAG_NO_EQUIP_COOLDOWN))
		return;

	for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) {
		_Spell const& spellData = pItem->GetProto()->Spells[i];

		// no spell
		if (!spellData.SpellId)
			continue;

		// wrong triggering type (note: ITEM_SPELLTRIGGER_ON_NO_DELAY_USE not have cooldown)
		if (spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
			continue;

		AddSpellCooldown(spellData.SpellId, pItem->GetEntry(), time(NULL) + 30);

		WorldPacket data(SMSG_ITEM_COOLDOWN, 12);
		data << pItem->GetGUID();
		data << uint32(spellData.SpellId);
		GetSession()->SendPacket(&data);
	}
}

void Player::resetSpells(bool myClassOnly) {
	// not need after this call
	if (HasAtLoginFlag(AT_LOGIN_RESET_SPELLS))
		RemoveAtLoginFlag(AT_LOGIN_RESET_SPELLS, true);

	// make full copy of map (spells removed and marked as deleted at another spell remove
	// and we can't use original map for safe iterative with visit each spell at loop end
	PlayerSpellMap smap = GetSpellMap();

	uint32 family;

	if (myClassOnly) {
		ChrClassesEntry const* clsEntry = sChrClassesStore.LookupEntry(
				getClass());
		if (!clsEntry)
			return;
		family = clsEntry->spellfamily;

		for (PlayerSpellMap::const_iterator iter = smap.begin();
				iter != smap.end(); ++iter) {
			SpellEntry const *spellInfo = sSpellStore.LookupEntry(iter->first);
			if (!spellInfo)
				continue;

			// skip server-side/triggered spells
			if (spellInfo->spellLevel == 0)
				continue;

			// skip wrong class/race skills
			if (!IsSpellFitByClassAndRace(spellInfo->Id))
				continue;

			// skip other spell families
			if (spellInfo->SpellFamilyName != family)
				continue;

			// skip spells with first rank learned as talent (and all talents then also)
			uint32 first_rank = sSpellMgr->GetFirstSpellInChain(spellInfo->Id);
			if (GetTalentSpellCost(first_rank) > 0)
				continue;

			// skip broken spells
			if (!SpellMgr::IsSpellValid(spellInfo, this, false))
				continue;
		}
	} else
		for (PlayerSpellMap::const_iterator iter = smap.begin();
				iter != smap.end(); ++iter)
			removeSpell(iter->first, false, false); // only iter->first can be accessed, object by iter->second can be deleted already

	learnDefaultSpells();
	learnQuestRewardedSpells();
}

void Player::learnDefaultSpells() {
	// learn default race/class spells
	PlayerInfo const *info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
	for (PlayerCreateInfoSpells::const_iterator itr = info->spell.begin();
			itr != info->spell.end(); ++itr) {
		uint32 tspell = *itr;
		sLog->outDebug(LOG_FILTER_PLAYER_LOADING,
				"PLAYER (Class: %u Race: %u): Adding initial spell, id = %u",
				uint32(getClass()), uint32(getRace()), tspell);
		if (!IsInWorld()) // will send in INITIAL_SPELLS in list anyway at map add
			addSpell(tspell, true, true, true, false);
		else
			// but send in normal spell in game learn case
			learnSpell(tspell, true);
	}
}

void Player::learnQuestRewardedSpells(Quest const* quest) {
	int32 spell_id = quest->GetRewSpellCast();
	uint32 src_spell_id = quest->GetSrcSpell();

	// skip quests without rewarded spell
	if (!spell_id)
		return;

	// if RewSpellCast = -1 we remove aura do to SrcSpell from player.
	if (spell_id == -1 && src_spell_id) {
		RemoveAurasDueToSpell(src_spell_id);
		return;
	}

	SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
	if (!spellInfo)
		return;

	// check learned spells state
	bool found = false;
	for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) {
		if (spellInfo->Effect[i] == SPELL_EFFECT_LEARN_SPELL
				&& !HasSpell(spellInfo->EffectTriggerSpell[i])) {
			found = true;
			break;
		}
	}

	// skip quests with not teaching spell or already known spell
	if (!found)
		return;

	// prevent learn non first rank unknown profession and second specialization for same profession)
	uint32 learned_0 = spellInfo->EffectTriggerSpell[0];
	if (sSpellMgr->GetSpellRank(learned_0) > 1 && !HasSpell(learned_0)) {
		// not have first rank learned (unlearned prof?)
		uint32 first_spell = sSpellMgr->GetFirstSpellInChain(learned_0);
		if (!HasSpell(first_spell))
			return;

		SpellEntry const *learnedInfo = sSpellStore.LookupEntry(learned_0);
		if (!learnedInfo)
			return;

		SpellsRequiringSpellMapBounds spellsRequired =
				sSpellMgr->GetSpellsRequiredForSpellBounds(learned_0);
		for (SpellsRequiringSpellMap::const_iterator itr2 = spellsRequired.first;
				itr2 != spellsRequired.second; ++itr2) {
			uint32 profSpell = itr2->second;

			// specialization
			if (learnedInfo->Effect[0] == SPELL_EFFECT_TRADE_SKILL
					&& learnedInfo->Effect[1] == 0 && profSpell) {
				// search other specialization for same prof
				for (PlayerSpellMap::const_iterator itr = m_spells.begin();
						itr != m_spells.end(); ++itr) {
					if (itr->second->state == PLAYERSPELL_REMOVED
							|| itr->first == learned_0)
						continue;

					SpellEntry const *itrInfo = sSpellStore.LookupEntry(
							itr->first);
					if (!itrInfo)
						return;

					// compare only specializations
					if (itrInfo->Effect[0] != SPELL_EFFECT_TRADE_SKILL
							|| itrInfo->Effect[1] != 0)
						continue;

					// compare same chain spells
					if (sSpellMgr->IsSpellRequiringSpell(itr->first, profSpell))
						return;
				}
			}
		}
	}

	CastSpell(this, spell_id, true);
}

void Player::learnQuestRewardedSpells() {
	// learn spells received from quest completing
	for (RewardedQuestSet::const_iterator itr = m_RewardedQuests.begin();
			itr != m_RewardedQuests.end(); ++itr) {
		Quest const* quest = sObjectMgr->GetQuestTemplate(*itr);
		if (!quest)
			continue;

		learnQuestRewardedSpells(quest);
	}
}

void Player::learnSkillRewardedSpells(uint32 skill_id, uint32 skill_value) {
	uint32 raceMask = getRaceMask();
	uint32 classMask = getClassMask();
	for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j) {
		SkillLineAbilityEntry const *pAbility =
				sSkillLineAbilityStore.LookupEntry(j);
		if (!pAbility || pAbility->skillId != skill_id
				|| pAbility->learnOnGetSkill
						!= ABILITY_LEARNED_ON_GET_PROFESSION_SKILL)
			continue;
		// Check race if set
		if (pAbility->racemask && !(pAbility->racemask & raceMask))
			continue;
		// Check class if set
		if (pAbility->classmask && !(pAbility->classmask & classMask))
			continue;

		if (sSpellStore.LookupEntry(pAbility->spellId)) {
			// need unlearn spell
			if (skill_value < pAbility->req_skill_value)
				removeSpell(pAbility->spellId);
			// need learn
			else if (!IsInWorld())
				addSpell(pAbility->spellId, true, true, true, false);
			else
				learnSpell(pAbility->spellId, true);
		}
	}
}

void Player::SendAurasForTarget(Unit *target) {
	// Client requires this packet on login to initialize so we can't skip it for self
	if (!target || (target->GetVisibleAuras()->empty() && target != this)) // speedup things
		return;

	WorldPacket data(SMSG_AURA_UPDATE_ALL);
	data.append(target->GetPackGUID());

	Unit::VisibleAuraMap const *visibleAuras = target->GetVisibleAuras();
	for (Unit::VisibleAuraMap::const_iterator itr = visibleAuras->begin();
			itr != visibleAuras->end(); ++itr) {
		AuraApplication * auraApp = itr->second;
		Aura * aura = auraApp->GetBase();
		data << uint8(auraApp->GetSlot());
		data << uint32(aura->GetId());

		// flags
		uint32 flags = auraApp->GetFlags();
		if (aura->GetMaxDuration() > 0)
			flags |= AFLAG_DURATION;
		data << uint8(flags);
		// level
		data << uint8(aura->GetCasterLevel());
		// charges
		data
				<< uint8(
						aura->GetStackAmount() > 1 ? aura->GetStackAmount() :
						(aura->GetCharges()) ? aura->GetCharges() : 1);

		if (!(flags & AFLAG_CASTER))
			data.appendPackGUID(aura->GetCasterGUID());

		if (flags & AFLAG_DURATION) // include aura duration
				{
			data << uint32(aura->GetMaxDuration());
			data << uint32(aura->GetDuration());
		}
	}

	GetSession()->SendPacket(&data);
}

void Player::SetDailyQuestStatus(uint32 quest_id) {
	if (Quest const* qQuest = sObjectMgr->GetQuestTemplate(quest_id)) {
		if (!qQuest->IsDFQuest()) {
			for (uint32 quest_daily_idx = 0;
					quest_daily_idx < PLAYER_MAX_DAILY_QUESTS;
					++quest_daily_idx) {
				if (!GetUInt32Value(
						PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx)) {
					SetUInt32Value(
							PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx,
							quest_id);
					m_lastDailyQuestTime = time(NULL); // last daily quest time
					m_DailyQuestChanged = true;
					break;
				}
			}
		} else {
			m_DFQuests.insert(quest_id);
			m_lastDailyQuestTime = time(NULL);
			m_DailyQuestChanged = true;
		}
	}
}

void Player::SetWeeklyQuestStatus(uint32 quest_id) {
	m_weeklyquests.insert(quest_id);
	m_WeeklyQuestChanged = true;
}

void Player::ResetDailyQuestStatus() {
	for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS;
			++quest_daily_idx)
		SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx, 0);

	m_DFQuests.clear(); // Dungeon Finder Quests.

	// DB data deleted in caller
	m_DailyQuestChanged = false;
	m_lastDailyQuestTime = 0;
}

void Player::ResetWeeklyQuestStatus() {
	if (m_weeklyquests.empty())
		return;

	m_weeklyquests.clear();
	// DB data deleted in caller
	m_WeeklyQuestChanged = false;
}

void Player::ResetCurrencyWeekCap() {
	for (PlayerCurrenciesMap::iterator itr = m_currencies.begin();
			itr != m_currencies.end(); ++itr)
		itr->second.weekCount = 0;

	// Calculating week cap for conquest points
	CharacterDatabase.Execute(
			"UPDATE character_cp_weekcap SET weekCap = ROUND(1.4326 * (1511.26 / (1 + 1639.28 / exp(0.00412 * `maxWeekRating`))) + 857.15) WHERE `source`=0 AND `maxWeekRating` BETWEEN 1500 AND 3000");
	CharacterDatabase.PExecute(
			"UPDATE character_cp_weekcap SET weekCap = '%u' WHERE `source`=0 AND `maxWeekRating` < 1500",
			PLAYER_DEFAULT_CONQUEST_POINTS_WEEK_CAP);
	CharacterDatabase.Execute(
			"UPDATE character_cp_weekcap SET weekCap =3000 WHERE `source`=0 AND `maxWeekRating` > 3000");
	CharacterDatabase.Execute(
			"UPDATE character_cp_weekcap SET maxWeekRating=0");

	if (m_maxWeekRating[CP_SOURCE_ARENA] <= 1500)
		m_conquestPointsWeekCap[CP_SOURCE_ARENA] =
				PLAYER_DEFAULT_CONQUEST_POINTS_WEEK_CAP;
	else if (m_maxWeekRating[CP_SOURCE_ARENA] > 3000)
		m_conquestPointsWeekCap[CP_SOURCE_ARENA] = 3000;
	else
		m_conquestPointsWeekCap[CP_SOURCE_ARENA] =
				uint16(
						1.4326
								* (1511.26
										/ (1
												+ 1639.28
														/ exp(
																0.00412
																		* m_maxWeekRating[CP_SOURCE_ARENA])))
								+ 857.15);

	m_maxWeekRating[CP_SOURCE_ARENA] = 0; // player must win at least 1 arena for week to change m_conquestPointsWeekCap

	_SaveConquestPointsWeekCap();
	_SaveCurrency();
	SendCurrencies();

	// Arena Teams
	for (uint32 arena_slot = 0; arena_slot < MAX_ARENA_SLOT; ++arena_slot) {
		uint32 arena_team_id = GetArenaTeamId(arena_slot);
		if (!arena_team_id)
			continue;
		ArenaTeam* arenaTeam = sObjectMgr->GetArenaTeamById(arena_team_id);
		arenaTeam->FinishWeek();
		arenaTeam->SaveToDB();
		arenaTeam->NotifyStatsChanged();
	}
}

void Player::UpdateMaxWeekRating(ConquestPointsSources source, uint8 slot) {
	m_maxWeekRating[source] = std::max(m_maxWeekRating[source],
			(uint16) GetArenaPersonalRating(slot));
}

Battleground* Player::GetBattleground() const {
	if (GetBattlegroundId() == 0)
		return NULL;

	return sBattlegroundMgr->GetBattleground(GetBattlegroundId(),
			m_bgData.bgTypeID);
}

bool Player::InArena() const {
	Battleground *bg = GetBattleground();
	if (!bg || !bg->isArena())
		return false;

	return true;
}

bool Player::GetBGAccessByLevel(BattlegroundTypeId bgTypeId) const {
	// get a template bg instead of running one
	Battleground *bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId);
	if (!bg)
		return false;

	// limit check leel to dbc compatible level range
	uint32 level = getLevel();
	if (level > DEFAULT_MAX_LEVEL)
		level = DEFAULT_MAX_LEVEL;

	if (level < bg->GetMinLevel() || level > bg->GetMaxLevel())
		return false;

	return true;
}

float Player::GetReputationPriceDiscount(Creature const* pCreature) const {
	FactionTemplateEntry const* vendor_faction =
			pCreature->getFactionTemplateEntry();
	if (!vendor_faction || !vendor_faction->faction)
		return 1.0f;

	ReputationRank rank = GetReputationRank(vendor_faction->faction);
	if (rank <= REP_NEUTRAL)
		return 1.0f;

	return 1.0f - 0.05f * (rank - REP_NEUTRAL);
}

bool Player::IsSpellFitByClassAndRace(uint32 spell_id) const {
	uint32 racemask = getRaceMask();
	uint32 classmask = getClassMask();

	SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(
			spell_id);
	if (bounds.first == bounds.second)
		return true;

	for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first;
			_spell_idx != bounds.second; ++_spell_idx) {
		// skip wrong race skills
		if (_spell_idx->second->racemask
				&& (_spell_idx->second->racemask & racemask) == 0)
			continue;

		// skip wrong class skills
		if (_spell_idx->second->classmask
				&& (_spell_idx->second->classmask & classmask) == 0)
			continue;

		return true;
	}

	return false;
}

bool Player::HasQuestForGO(int32 GOId) const {
	for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i) {
		uint32 questid = GetQuestSlotQuestId(i);
		if (questid == 0)
			continue;

		QuestStatusMap::const_iterator qs_itr = m_QuestStatus.find(questid);
		if (qs_itr == m_QuestStatus.end())
			continue;

		QuestStatusData const& qs = qs_itr->second;

		if (qs.m_status == QUEST_STATUS_INCOMPLETE) {
			Quest const* qinfo = sObjectMgr->GetQuestTemplate(questid);
			if (!qinfo)
				continue;

			if (GetGroup() && GetGroup()->isRaidGroup()
					&& !qinfo->IsAllowedInRaid())
				continue;

			for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j) {
				if (qinfo->ReqCreatureOrGOId[j] >= 0) //skip non GO case
					continue;

				if ((-1) * GOId == qinfo->ReqCreatureOrGOId[j]
						&& qs.m_creatureOrGOcount[j]
								< qinfo->ReqCreatureOrGOCount[j])
					return true;
			}
		}
	}
	return false;
}

void Player::UpdateForQuestWorldObjects() {
	if (m_clientGUIDs.empty())
		return;

	UpdateData udata;
	udata.m_map = uint16(GetMapId());
	WorldPacket packet;
	for (ClientGUIDs::iterator itr = m_clientGUIDs.begin();
			itr != m_clientGUIDs.end(); ++itr) {
		if (IS_GAMEOBJECT_GUID(*itr)) {
			if (GameObject * obj = HashMapHolder<GameObject>::Find(*itr))
				obj->BuildValuesUpdateBlockForPlayer(&udata, this);
		} else if (IS_CRE_OR_VEH_GUID(*itr)) {
			Creature *obj = ObjectAccessor::GetCreatureOrPetOrVehicle(*this,
					*itr);
			if (!obj)
				continue;

			// check if this unit requires quest specific flags
			if (!obj->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK))
				continue;

			SpellClickInfoMapBounds clickPair =
					sObjectMgr->GetSpellClickInfoMapBounds(obj->GetEntry());
			for (SpellClickInfoMap::const_iterator _itr = clickPair.first;
					_itr != clickPair.second; ++_itr)
				if (_itr->second.questStart || _itr->second.questEnd) {
					obj->BuildCreateUpdateBlockForPlayer(&udata, this);
					break;
				}
		}
	}
	udata.BuildPacket(&packet);
	GetSession()->SendPacket(&packet);
}

void Player::SummonIfPossible(bool agree) {
	if (!agree) {
		m_summon_expire = 0;
		return;
	}

	// expire and auto declined
	if (m_summon_expire < time(NULL))
		return;

	// stop taxi flight at summon
	if (isInFlight()) {
		GetMotionMaster()->MovementExpired();
		CleanupAfterTaxiFlight();
	}

	// drop flag at summon
	// this code can be reached only when GM is summoning player who carries flag, because player should be immune to summoning spells when he carries flag
	if (Battleground *bg = GetBattleground())
		bg->EventPlayerDroppedFlag(this);

	m_summon_expire = 0;

	GetAchievementMgr().UpdateAchievementCriteria(
			ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS, 1);

	TeleportTo(m_summon_mapid, m_summon_x, m_summon_y, m_summon_z,
			GetOrientation());
}

void Player::RemoveItemDurations(Item *item) {
	for (ItemDurationList::iterator itr = m_itemDuration.begin();
			itr != m_itemDuration.end(); ++itr) {
		if (*itr == item) {
			m_itemDuration.erase(itr);
			break;
		}
	}
}

void Player::AddItemDurations(Item *item) {
	if (item->GetUInt32Value(ITEM_FIELD_DURATION)) {
		m_itemDuration.push_back(item);
		item->SendTimeUpdate(this);
	}
}

void Player::AutoUnequipOffhandIfNeed(bool force /*= false*/) {
	Item *offItem = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
	if (!offItem)
		return;

	// unequip offhand weapon if player doesn't have dual wield anymore
	if (!CanDualWield()
			&& (offItem->GetProto()->InventoryType == INVTYPE_WEAPONOFFHAND
					|| offItem->GetProto()->InventoryType == INVTYPE_WEAPON))
		force = true;

	// need unequip offhand for 2h-weapon without TitanGrip (in any from hands)
	if (!force
			&& (CanTitanGrip()
					|| (offItem->GetProto()->InventoryType != INVTYPE_2HWEAPON
							&& !IsTwoHandUsed())))
		return;

	ItemPosCountVec off_dest;
	uint8 off_msg = CanStoreItem(NULL_BAG, NULL_SLOT, off_dest, offItem, false);
	if (off_msg == EQUIP_ERR_OK) {
		RemoveItem(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
		StoreItem(off_dest, offItem, true);
	} else {
		MoveItemFromInventory(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND,
				true);
		SQLTransaction trans = CharacterDatabase.BeginTransaction();
		offItem->DeleteFromInventoryDB(trans); // deletes item from character's inventory
		offItem->SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone

		std::string subject = GetSession()->GetArkCoreString(
				LANG_NOT_EQUIPPED_ITEM);
		MailDraft(subject,
				"There were problems with equipping one or several items").AddItem(
				offItem).SendMailTo(trans, this,
				MailSender(this, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_COPIED);

		CharacterDatabase.CommitTransaction(trans);
	}
}

OutdoorPvP * Player::GetOutdoorPvP() const {
	return sOutdoorPvPMgr->GetOutdoorPvPToZoneId(GetZoneId());
}

bool Player::HasItemFitToSpellRequirements(SpellEntry const* spellInfo,
		Item const* ignoreItem) {
	if (spellInfo->EquippedItemClass < 0)
		return true;

	// scan other equipped items for same requirements (mostly 2 daggers/etc)
	// for optimize check 2 used cases only
	switch (spellInfo->EquippedItemClass) {
	case ITEM_CLASS_WEAPON: {
		for (uint8 i = EQUIPMENT_SLOT_MAINHAND; i < EQUIPMENT_SLOT_TABARD; ++i)
			if (Item *item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, i))
				if (item != ignoreItem
						&& item->IsFitToSpellRequirements(spellInfo))
					return true;
		break;
	}
	case ITEM_CLASS_ARMOR: {
		// tabard not have dependent spells
		for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_MAINHAND; ++i)
			if (Item *item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, i))
				if (item != ignoreItem
						&& item->IsFitToSpellRequirements(spellInfo))
					return true;

		// shields can be equipped to offhand slot
		if (Item *item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND))
			if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
				return true;

		// ranged slot can have some armor subclasses
		if (Item *item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
			if (item != ignoreItem && item->IsFitToSpellRequirements(spellInfo))
				return true;

		break;
	}
	default:
		sLog->outError(
				"HasItemFitToSpellRequirements: Not handled spell requirement for item class %u",
				spellInfo->EquippedItemClass);
		break;
	}

	return false;
}

bool Player::CanNoReagentCast(SpellEntry const* spellInfo) const {
	// don't take reagents for spells with SPELL_ATTR5_NO_REAGENT_WHILE_PREP
	if (spellInfo->AttributesEx5 & SPELL_ATTR5_NO_REAGENT_WHILE_PREP
			&& HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION))
		return true;

	// Check no reagent use mask
	flag96 noReagentMask;
	noReagentMask[0] = GetUInt32Value(PLAYER_NO_REAGENT_COST_1);
	noReagentMask[1] = GetUInt32Value(PLAYER_NO_REAGENT_COST_1 + 1);
	noReagentMask[2] = GetUInt32Value(PLAYER_NO_REAGENT_COST_1 + 2);
	if (spellInfo->SpellFamilyFlags & noReagentMask)
		return true;

	return false;
}

void Player::RemoveItemDependentAurasAndCasts(Item * pItem) {
	for (AuraMap::iterator itr = m_ownedAuras.begin();
			itr != m_ownedAuras.end();) {
		Aura * aura = itr->second;

		// skip passive (passive item dependent spells work in another way) and not self applied auras
		SpellEntry const* spellInfo = aura->GetSpellProto();
		if (aura->IsPassive() || aura->GetCasterGUID() != GetGUID()) {
			++itr;
			continue;
		}

		// skip if not item dependent or have alternative item
		if (HasItemFitToSpellRequirements(spellInfo, pItem)) {
			++itr;
			continue;
		}

		// no alt item, remove aura, restart check
		RemoveOwnedAura(itr);
	}

	// currently casted spells can be dependent from item
	for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i)
		if (Spell* spell = GetCurrentSpell(CurrentSpellTypes(i)))
			if (spell->getState() != SPELL_STATE_DELAYED
					&& !HasItemFitToSpellRequirements(spell->m_spellInfo, pItem))
				InterruptSpell(CurrentSpellTypes(i));
}

uint32 Player::GetResurrectionSpellId() {
	// search priceless resurrection possibilities
	uint32 prio = 0;
	uint32 spell_id = 0;
	AuraEffectList const& dummyAuras = GetAuraEffectsByType(SPELL_AURA_DUMMY);
	for (AuraEffectList::const_iterator itr = dummyAuras.begin();
			itr != dummyAuras.end(); ++itr) {
		// Soulstone Resurrection                           // prio: 3 (max, non death persistent)
		if (prio < 2 && (*itr)->GetSpellProto()->SpellVisual[0] == 99
				&& (*itr)->GetSpellProto()->SpellIconID == 92) {
			switch ((*itr)->GetId()) {
			case 20707:
				spell_id = 3026;
				break; // rank 1
			case 20762:
				spell_id = 20758;
				break; // rank 2
			case 20763:
				spell_id = 20759;
				break; // rank 3
			case 20764:
				spell_id = 20760;
				break; // rank 4
			case 20765:
				spell_id = 20761;
				break; // rank 5
			case 27239:
				spell_id = 27240;
				break; // rank 6
			case 47883:
				spell_id = 47882;
				break; // rank 7
			default:
				sLog->outError("Unhandled spell %u: S.Resurrection",
						(*itr)->GetId());
				continue;
			}

			prio = 3;
		}
		// Twisting Nether                                  // prio: 2 (max)
		else if ((*itr)->GetId() == 23701 && roll_chance_i(10)) {
			prio = 2;
			spell_id = 23700;
		}
	}

	// Reincarnation (passive spell)  // prio: 1                  // Glyph of Renewed Life
	if (prio < 1 && HasSpell(20608) && !HasSpellCooldown(21169)
			&& (HasAura(58059) || HasItemCount(17030, 1)))
		spell_id = 21169;

	return spell_id;
}

// Used in triggers for check "Only to targets that grant experience or honor" req
bool Player::isHonorOrXPTarget(Unit* pVictim) {
	uint8 v_level = pVictim->getLevel();
	uint8 k_grey = Trinity::XP::GetGrayLevel(getLevel());

	// Victim level less gray level
	if (v_level <= k_grey)
		return false;

	if (pVictim->GetTypeId() == TYPEID_UNIT) {
		if (pVictim->ToCreature()->isTotem() || pVictim->ToCreature()->isPet()
				|| pVictim->ToCreature()->GetCreatureInfo()->flags_extra
						& CREATURE_FLAG_EXTRA_NO_XP_AT_KILL)
			return false;
	}
	return true;
}

bool Player::GetsRecruitAFriendBonus(bool forXP) {
	bool recruitAFriend = false;
	if (getLevel()
			<= sWorld->getIntConfig(
					CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL) || !forXP) {
		if (Group *group = this->GetGroup()) {
			for (GroupReference *itr = group->GetFirstMember(); itr != NULL;
					itr = itr->next()) {
				Player* pGroupGuy = itr->getSource();
				if (!pGroupGuy)
					continue;

				if (!pGroupGuy->IsAtRecruitAFriendDistance(this))
					continue; // member (alive or dead) or his corpse at req. distance

				if (forXP) {
					// level must be allowed to get RaF bonus
					if (pGroupGuy->getLevel()
							> sWorld->getIntConfig(
									CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL))
						continue;

					// level difference must be small enough to get RaF bonus, UNLESS we are lower level
					if (pGroupGuy->getLevel() < getLevel())
						if (uint8(getLevel() - pGroupGuy->getLevel())
								> sWorld->getIntConfig(
										CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL_DIFFERENCE))
							continue;
				}

				bool ARecruitedB = (pGroupGuy->GetSession()->GetRecruiterId()
						== GetSession()->GetAccountId());
				bool BRecruitedA = (GetSession()->GetRecruiterId()
						== pGroupGuy->GetSession()->GetAccountId());
				if (ARecruitedB || BRecruitedA) {
					recruitAFriend = true;
					break;
				}
			}
		}
	}
	return recruitAFriend;
}

bool Player::RewardPlayerAndGroupAtKill(Unit* pVictim) {
	bool PvP = pVictim->isCharmedOwnedByPlayerOrPlayer();

	// prepare data for near group iteration (PvP and !PvP cases)
	uint32 xp = 0;
	bool honored_kill = false;

	if (Group *pGroup = GetGroup()) {
		uint32 count = 0;
		uint32 sum_level = 0;
		Player* member_with_max_level = NULL;
		Player* not_gray_member_with_max_level = NULL;

		pGroup->GetDataForXPAtKill(pVictim, count, sum_level,
				member_with_max_level, not_gray_member_with_max_level);

		if (member_with_max_level) {
			// PvP kills doesn't yield experience
			// also no XP gained if there is no member below gray level
			xp = (PvP || !not_gray_member_with_max_level || GetVehicle()) ?
					0 :
					Trinity::XP::Gain(not_gray_member_with_max_level, pVictim);

			/// skip in check PvP case (for speed, not used)
			bool is_raid =
					PvP ? false : sMapStore.LookupEntry(GetMapId())->IsRaid()
									&& pGroup->isRaidGroup();
			bool is_dungeon =
					PvP ? false : sMapStore.LookupEntry(GetMapId())->IsDungeon();
			float group_rate = Trinity::XP::xp_in_group_rate(count, is_raid);

			for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL;
					itr = itr->next()) {
				Player* pGroupGuy = itr->getSource();
				if (!pGroupGuy)
					continue;

				if (!pGroupGuy->IsAtGroupRewardDistance(pVictim))
					continue; // member (alive or dead) or his corpse at req. distance

				// honor can be in PvP and !PvP (racial leader) cases (for alive)
				if (pGroupGuy->isAlive()
						&& pGroupGuy->RewardHonor(pVictim, count, -1, true)
						&& pGroupGuy == this)
					honored_kill = true;

				// xp and reputation only in !PvP case
				if (!PvP) {
					float rate = group_rate * float(pGroupGuy->getLevel())
							/ sum_level;

					// if is in dungeon then all receive full reputation at kill
					// rewarded any alive/dead/near_corpse group member
					pGroupGuy->RewardReputation(pVictim,
							is_dungeon ? 1.0f : rate);

					// XP updated only for alive group member
					if (pGroupGuy->isAlive() && not_gray_member_with_max_level
							&& pGroupGuy->getLevel()
									<= not_gray_member_with_max_level->getLevel()) {
						uint32 itr_xp =
								(member_with_max_level
										== not_gray_member_with_max_level) ?
										uint32(xp * rate) :
										uint32((xp * rate / 2) + 1);

						// handle SPELL_AURA_MOD_XP_PCT auras
						Unit::AuraEffectList const& ModXPPctAuras =
								GetAuraEffectsByType(SPELL_AURA_MOD_XP_PCT);
						for (Unit::AuraEffectList::const_iterator i =
								ModXPPctAuras.begin(); i != ModXPPctAuras.end();
								++i)
							itr_xp =
									uint32(
											itr_xp
													* (1.0f
															+ (*i)->GetAmount()
																	/ 100.0f));

						pGroupGuy->GiveXP(itr_xp, pVictim, group_rate);
						if (Pet* pet = pGroupGuy->GetPet())
							pet->GivePetXP(itr_xp / 2);
					}

					// quest objectives updated only for alive group member or dead but with not released body
					if (pGroupGuy->isAlive() || !pGroupGuy->GetCorpse()) {
						// normal creature (not pet/etc) can be only in !PvP case
						if (pVictim->GetTypeId() == TYPEID_UNIT)
							pGroupGuy->KilledMonster(
									pVictim->ToCreature()->GetCreatureInfo(),
									pVictim->GetGUID());
					}
				}
			}
		}
	} else // if (!pGroup)
	{
		xp = (PvP || GetVehicle()) ? 0 : Trinity::XP::Gain(this, pVictim);

		// honor can be in PvP and !PvP (racial leader) cases
		if (RewardHonor(pVictim, 1, -1, true))
			honored_kill = true;

		// xp and reputation only in !PvP case
		if (!PvP) {
			RewardReputation(pVictim, 1);

			// handle SPELL_AURA_MOD_XP_PCT auras
			Unit::AuraEffectList const& ModXPPctAuras = GetAuraEffectsByType(
					SPELL_AURA_MOD_XP_PCT);
			for (Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin();
					i != ModXPPctAuras.end(); ++i)
				xp = uint32(xp * (1.0f + (*i)->GetAmount() / 100.0f));

			GiveXP(xp, pVictim);

			if (Pet* pet = GetPet())
				pet->GivePetXP(xp);

			// normal creature (not pet/etc) can be only in !PvP case
			if (pVictim->GetTypeId() == TYPEID_UNIT)
				KilledMonster(pVictim->ToCreature()->GetCreatureInfo(),
						pVictim->GetGUID());
		}
	}

	// Credit encounter in instance
	if (Creature* victim = pVictim->ToCreature())
		if (victim->IsDungeonBoss())
			if (InstanceScript* instance = pVictim->GetInstanceScript())
				instance->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE,
						pVictim->GetEntry(), pVictim);

	return xp || honored_kill;
}

void Player::RewardPlayerAndGroupAtEvent(uint32 creature_id,
		WorldObject* pRewardSource) {
	if (!pRewardSource)
		return;
	uint64 creature_guid =
			(pRewardSource->GetTypeId() == TYPEID_UNIT) ?
					pRewardSource->GetGUID() : uint64(0);

	// prepare data for near group iteration
	if (Group *pGroup = GetGroup()) {
		for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr =
				itr->next()) {
			Player* pGroupGuy = itr->getSource();
			if (!pGroupGuy)
				continue;

			if (!pGroupGuy->IsAtGroupRewardDistance(pRewardSource))
				continue; // member (alive or dead) or his corpse at req. distance

			// quest objectives updated only for alive group member or dead but with not released body
			if (pGroupGuy->isAlive() || !pGroupGuy->GetCorpse())
				pGroupGuy->KilledMonsterCredit(creature_id, creature_guid);
		}
	} else
		// if (!pGroup)
		KilledMonsterCredit(creature_id, creature_guid);
}

bool Player::IsAtGroupRewardDistance(WorldObject const* pRewardSource) const {
	if (!pRewardSource)
		return false;
	const WorldObject* player = GetCorpse();
	if (!player || isAlive())
		player = this;

	if (player->GetMapId() != pRewardSource->GetMapId()
			|| player->GetInstanceId() != pRewardSource->GetInstanceId())
		return false;

	return pRewardSource->GetDistance(player)
			<= sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE);
}

bool Player::IsAtRecruitAFriendDistance(WorldObject const* pOther) const {
	if (!pOther)
		return false;
	const WorldObject* player = GetCorpse();
	if (!player || isAlive())
		player = this;

	if (player->GetMapId() != pOther->GetMapId()
			|| player->GetInstanceId() != pOther->GetInstanceId())
		return false;

	return pOther->GetDistance(player)
			<= sWorld->getFloatConfig(CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE);
}

uint32 Player::GetBaseWeaponSkillValue(WeaponAttackType attType) const {
	Item* item = GetWeaponForAttack(attType, true);

	// unarmed only with base attack
	if (attType != BASE_ATTACK && !item)
		return 0;

	// weapon skill or (unarmed for base attack and for fist weapons)
	uint32 skill =
			(item && item->GetSkill() != SKILL_FIST_WEAPONS) ?
					item->GetSkill() : uint32(SKILL_UNARMED);
	return GetBaseSkillValue(skill);
}

void Player::ResurectUsingRequestData() {
	/// Teleport before resurrecting by player, otherwise the player might get attacked from creatures near his corpse
	if (IS_PLAYER_GUID(m_resurrectGUID))
		TeleportTo(m_resurrectMap, m_resurrectX, m_resurrectY, m_resurrectZ,
				GetOrientation());

	//we cannot resurrect player when we triggered far teleport
	//player will be resurrected upon teleportation
	if (IsBeingTeleportedFar()) {
		ScheduleDelayedOperation(DELAYED_RESURRECT_PLAYER);
		return;
	}

	ResurrectPlayer(0.0f, false);

	if (GetMaxHealth() > m_resurrectHealth)
		SetHealth(m_resurrectHealth);
	else
		SetFullHealth();

	if (GetMaxPower(POWER_MANA) > m_resurrectMana)
		SetPower(POWER_MANA, m_resurrectMana);
	else
		SetPower(POWER_MANA, GetMaxPower(POWER_MANA));

	SetPower(POWER_RAGE, 0);

	SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));

	SpawnCorpseBones();
}

void Player::SetClientControl(Unit* target, uint8 allowMove) {
	WorldPacket data(SMSG_CLIENT_CONTROL_UPDATE,
			target->GetPackGUID().size() + 1);
	data.append(target->GetPackGUID());
	data << uint8(allowMove);
	GetSession()->SendPacket(&data);
	if (target == this)
		SetMover(this);
}

void Player::UpdateZoneDependentAuras(uint32 newZone) {
	// Some spells applied at enter into zone (with subzones), aura removed in UpdateAreaDependentAuras that called always at zone->area update
	SpellAreaForAreaMapBounds saBounds =
			sSpellMgr->GetSpellAreaForAreaMapBounds(newZone);
	for (SpellAreaForAreaMap::const_iterator itr = saBounds.first;
			itr != saBounds.second; ++itr)
		if (itr->second->autocast
				&& itr->second->IsFitToRequirements(this, newZone, 0))
			if (!HasAura(itr->second->spellId))
				CastSpell(this, itr->second->spellId, true);
}

void Player::UpdateAreaDependentAuras(uint32 newArea) {
	// remove auras from spells with area limitations
	for (AuraMap::iterator iter = m_ownedAuras.begin();
			iter != m_ownedAuras.end();) {
		// use m_zoneUpdateId for speed: UpdateArea called from UpdateZone or instead UpdateZone in both cases m_zoneUpdateId up-to-date
		if (sSpellMgr->GetSpellAllowedInLocationError(
				iter->second->GetSpellProto(), GetMapId(), m_zoneUpdateId,
				newArea, this) != SPELL_CAST_OK)
			RemoveOwnedAura(iter);
		else
			++iter;
	}

	// some auras applied at subzone enter
	SpellAreaForAreaMapBounds saBounds =
			sSpellMgr->GetSpellAreaForAreaMapBounds(newArea);
	for (SpellAreaForAreaMap::const_iterator itr = saBounds.first;
			itr != saBounds.second; ++itr)
		if (itr->second->autocast
				&& itr->second->IsFitToRequirements(this, m_zoneUpdateId,
						newArea))
			if (!HasAura(itr->second->spellId))
				CastSpell(this, itr->second->spellId, true);

	if (newArea == 4273 && GetVehicle() && GetPositionX() > 400) // Ulduar
			{
		switch (GetVehicleBase()->GetEntry()) {
		case 33062:
		case 33109:
		case 33060:
			GetVehicle()->Dismiss();
			break;
		}
	}
}

uint32 Player::GetCorpseReclaimDelay(bool pvp) const {
	if (pvp) {
		if (!sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP))
			return copseReclaimDelay[0];
	} else if (!sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE))
		return 0;

	time_t now = time(NULL);
	// 0..2 full period
	// should be ceil(x)-1 but not floor(x)
	uint64 count =
			(now < m_deathExpireTime - 1) ?
					(m_deathExpireTime - 1 - now) / DEATH_EXPIRE_STEP : 0;
	return copseReclaimDelay[count];
}

void Player::UpdateCorpseReclaimDelay() {
	bool pvp = m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH;

	if ((pvp && !sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP))
			|| (!pvp
					&& !sWorld->getBoolConfig(
							CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE)))
		return;

	time_t now = time(NULL);
	if (now < m_deathExpireTime) {
		// full and partly periods 1..3
		uint64 count = (m_deathExpireTime - now) / DEATH_EXPIRE_STEP + 1;
		if (count < MAX_DEATH_COUNT)
			m_deathExpireTime = now + (count + 1) * DEATH_EXPIRE_STEP;
		else
			m_deathExpireTime = now + MAX_DEATH_COUNT * DEATH_EXPIRE_STEP;
	} else
		m_deathExpireTime = now + DEATH_EXPIRE_STEP;
}

void Player::SendCorpseReclaimDelay(bool load) {
	Corpse* corpse = GetCorpse();
	if (load && !corpse)
		return;

	bool pvp;
	if (corpse)
		pvp = (corpse->GetType() == CORPSE_RESURRECTABLE_PVP);
	else
		pvp = (m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH);

	time_t delay;
	if (load) {
		if (corpse->GetGhostTime() > m_deathExpireTime)
			return;

		uint64 count;
		if ((pvp && sWorld->getBoolConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP))
				|| (!pvp
						&& sWorld->getBoolConfig(
								CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE))) {
			count = (m_deathExpireTime - corpse->GetGhostTime())
					/ DEATH_EXPIRE_STEP;
			if (count >= MAX_DEATH_COUNT)
				count = MAX_DEATH_COUNT - 1;
		} else
			count = 0;

		time_t expected_time = corpse->GetGhostTime()
				+ copseReclaimDelay[count];

		time_t now = time(NULL);
		if (now >= expected_time)
			return;

		delay = expected_time - now;
	} else
		delay = GetCorpseReclaimDelay(pvp);

	if (!delay)
		return;

	//! corpse reclaim delay 30 * 1000ms or longer at often deaths
	WorldPacket data(SMSG_CORPSE_RECLAIM_DELAY, 4);
	data << uint32(delay * IN_MILLISECONDS);
	GetSession()->SendPacket(&data);
}

Player* Player::GetNextRandomRaidMember(float radius) {
	Group *pGroup = GetGroup();
	if (!pGroup)
		return NULL;

	std::vector<Player*> nearMembers;
	nearMembers.reserve(pGroup->GetMembersCount());

	for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL;
			itr = itr->next()) {
		Player* Target = itr->getSource();

		// IsHostileTo check duel and controlled by enemy
		if (Target && Target != this && IsWithinDistInMap(Target, radius)
				&& !Target->HasInvisibilityAura() && !IsHostileTo(Target))
			nearMembers.push_back(Target);
	}

	if (nearMembers.empty())
		return NULL;

	uint32 randTarget = urand(0, nearMembers.size() - 1);
	return nearMembers[randTarget];
}

PartyResult Player::CanUninviteFromGroup() const {
	const Group* grp = GetGroup();
	if (!grp)
		return ERR_NOT_IN_GROUP;

	if (grp->isLFGGroup()) {
		uint64 gguid = grp->GetGUID();
		if (!sLFGMgr->GetKicksLeft(gguid))
			return ERR_PARTY_LFG_BOOT_LIMIT;

		LfgState state = sLFGMgr->GetState(gguid);
		if (state == LFG_STATE_BOOT)
			return ERR_PARTY_LFG_BOOT_IN_PROGRESS;

		if (grp->GetMembersCount() <= sLFGMgr->GetVotesNeeded(gguid))
			return ERR_PARTY_LFG_BOOT_TOO_FEW_PLAYERS;

		if (state == LFG_STATE_FINISHED_DUNGEON)
			return ERR_PARTY_LFG_BOOT_DUNGEON_COMPLETE;

		if (grp->isRollLootActive())
			return ERR_PARTY_LFG_BOOT_LOOT_ROLLS;

		/* Missing support for these types
		 return ERR_PARTY_LFG_BOOT_IN_COMBAT; // also have a cooldown (some secs after combat finish
		 return ERR_PARTY_LFG_BOOT_COOLDOWN_S;
		 return ERR_PARTY_LFG_BOOT_NOT_ELIGIBLE_S;
		 */
	} else {
		if (!grp->IsLeader(GetGUID()) && !grp->IsAssistant(GetGUID()))
			return ERR_NOT_LEADER;

		if (InBattleground())
			return ERR_INVITE_RESTRICTED;
	}

	return ERR_PARTY_RESULT_OK;
}

bool Player::isUsingLfg() {
	uint64 guid = GetGUID();
	return sLFGMgr->GetState(guid) != LFG_STATE_NONE;
}

void Player::SetBattlegroundRaid(Group* group, int8 subgroup) {
	//we must move references from m_group to m_originalGroup
	SetOriginalGroup(GetGroup(), GetSubGroup());

	m_group.unlink();
	m_group.link(group, this);
	m_group.setSubGroup((uint8) subgroup);
}

void Player::RemoveFromBattlegroundRaid() {
	//remove existing reference
	m_group.unlink();
	if (Group* group = GetOriginalGroup()) {
		m_group.link(group, this);
		m_group.setSubGroup(GetOriginalSubGroup());
	}
	SetOriginalGroup(NULL);
}

void Player::SetOriginalGroup(Group *group, int8 subgroup) {
	if (group == NULL)
		m_originalGroup.unlink();
	else {
		// never use SetOriginalGroup without a subgroup unless you specify NULL for group
		ASSERT(subgroup >= 0);
		m_originalGroup.link(group, this);
		m_originalGroup.setSubGroup((uint8) subgroup);
	}
}

void Player::UpdateUnderwaterState(Map* m, float x, float y, float z) {
	LiquidData liquid_status;
	ZLiquidStatus res = m->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS,
			&liquid_status);
	if (!res) {
		m_MirrorTimerFlags &= ~(UNDERWATER_INWATER | UNDERWATER_INLAVA
				| UNDERWATER_INSLIME | UNDERWARER_INDARKWATER);
		// Small hack for enable breath in WMO
		/* if (IsInWater())
		 m_MirrorTimerFlags|=UNDERWATER_INWATER; */
		return;
	}

	// All liquids type - check under water position
	if (liquid_status.type
			& (MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN
					| MAP_LIQUID_TYPE_MAGMA | MAP_LIQUID_TYPE_SLIME)) {
		if (res & LIQUID_MAP_UNDER_WATER)
			m_MirrorTimerFlags |= UNDERWATER_INWATER;
		else
			m_MirrorTimerFlags &= ~UNDERWATER_INWATER;
	}

	// Allow travel in dark water on taxi or transport
	if ((liquid_status.type & MAP_LIQUID_TYPE_DARK_WATER) && !isInFlight()
			&& !GetTransport())
		m_MirrorTimerFlags |= UNDERWARER_INDARKWATER;
	else
		m_MirrorTimerFlags &= ~UNDERWARER_INDARKWATER;

	// in lava check, anywhere in lava level
	if (liquid_status.type & MAP_LIQUID_TYPE_MAGMA) {
		if (res
				& (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER
						| LIQUID_MAP_WATER_WALK))
			m_MirrorTimerFlags |= UNDERWATER_INLAVA;
		else
			m_MirrorTimerFlags &= ~UNDERWATER_INLAVA;
	}
	// in slime check, anywhere in slime level
	if (liquid_status.type & MAP_LIQUID_TYPE_SLIME) {
		if (res
				& (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER
						| LIQUID_MAP_WATER_WALK))
			m_MirrorTimerFlags |= UNDERWATER_INSLIME;
		else
			m_MirrorTimerFlags &= ~UNDERWATER_INSLIME;
	}
}

void Player::SetCanParry(bool value) {
	if (m_canParry == value)
		return;

	m_canParry = value;
	UpdateParryPercentage();
}

void Player::SetCanBlock(bool value) {
	if (m_canBlock == value)
		return;

	m_canBlock = value;
	UpdateBlockPercentage();
}

bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const {
	for (ItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end();
			++itr)
		if (itr->pos == pos)
			return true;
	return false;
}

// ***********************************
// -------------ARKCORE---------------
// ***********************************

void Player::StopCastingBindSight() {
	if (WorldObject* target = GetViewpoint()) {
		if (target->isType(TYPEMASK_UNIT)) {
			((Unit*) target)->RemoveAura(SPELL_AURA_BIND_SIGHT, GetGUID());
			((Unit*) target)->RemoveAura(SPELL_AURA_MOD_POSSESS, GetGUID());
			((Unit*) target)->RemoveAura(SPELL_AURA_MOD_POSSESS_PET, GetGUID());
		}
	}
}

void Player::SetViewpoint(WorldObject* target, bool apply) {
	if (apply) {
		sLog->outDebug(
				LOG_FILTER_MAPS,
				"Player::CreateViewpoint: Player %s create seer %u (TypeId: %u).",
				GetName(), target->GetEntry(), target->GetTypeId());

		if (!AddUInt64Value(PLAYER_FARSIGHT, target->GetGUID())) {
			sLog->outCrash(
					"Player::CreateViewpoint: Player %s cannot add new viewpoint!",
					GetName());
			return;
		}

		// farsight dynobj or puppet may be very far away
		UpdateVisibilityOf(target);

		if (target->isType(TYPEMASK_UNIT) && !GetVehicle())
			((Unit*) target)->AddPlayerToVision(this);
	} else {
		sLog->outDebug(LOG_FILTER_MAPS,
				"Player::CreateViewpoint: Player %s remove seer", GetName());

		if (!RemoveUInt64Value(PLAYER_FARSIGHT, target->GetGUID())) {
			sLog->outCrash(
					"Player::CreateViewpoint: Player %s cannot remove current viewpoint!",
					GetName());
			return;
		}

		if (target->isType(TYPEMASK_UNIT) && !GetVehicle())
			((Unit*) target)->RemovePlayerFromVision(this);

		//must immediately set seer back otherwise may crash
		m_seer = this;

		//WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0);
		//GetSession()->SendPacket(&data);
	}
}

WorldObject* Player::GetViewpoint() const {
	if (uint64 guid = GetUInt64Value(PLAYER_FARSIGHT))
		return (WorldObject*) ObjectAccessor::GetObjectByTypeMask(*this, guid,
				TYPEMASK_SEER);
	return NULL;
}

bool Player::CanUseBattlegroundObject() {
	// TODO : some spells gives player ForceReaction to one faction (ReputationMgr::ApplyForceReaction)
	// maybe gameobject code should handle that ForceReaction usage
	// BUG: sometimes when player clicks on flag in AB - client won't send gameobject_use, only gameobject_report_use packet
	return ( //InBattleground() &&                          // in battleground - not need, check in other cases
			 //!IsMounted() && - not correct, player is dismounted when he clicks on flag
			 //player cannot use object when he is invulnerable (immune)
	!isTotalImmune() && // not totally immune
			//i'm not sure if these two are correct, because invisible players should get visible when they click on flag
			!HasStealthAura() && // not stealthed
			!HasInvisibilityAura() && // not invisible
			!HasAura(SPELL_RECENTLY_DROPPED_FLAG) && // can't pickup
			isAlive() // live player
	);
}

bool Player::CanCaptureTowerPoint() {
	return (!HasStealthAura() && // not stealthed
			!HasInvisibilityAura() && // not invisible
			isAlive() // live player
	);
}

uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor,
		uint8 newfacialhair, BarberShopStyleEntry const* newSkin)

		{
	uint8 level = getLevel();

	if (level > GT_MAX_LEVEL)
		level = GT_MAX_LEVEL; // max level in this dbc

	uint8 hairstyle = GetByteValue(PLAYER_BYTES, 2);
	uint8 haircolor = GetByteValue(PLAYER_BYTES, 3);
	uint8 facialhair = GetByteValue(PLAYER_BYTES_2, 0);
	uint8 skincolor = GetByteValue(PLAYER_BYTES, 0);

	if ((hairstyle == newhairstyle) && (haircolor == newhaircolor)
			&& (facialhair == newfacialhair)
			&& (!newSkin || (newSkin->hair_id == skincolor)))
		return 0;

	GtBarberShopCostBaseEntry const *bsc =
			sGtBarberShopCostBaseStore.LookupEntry(level - 1);

	if (!bsc) // shouldn't happen
		return 0xFFFFFFFF;

	float cost = 0;

	if (hairstyle != newhairstyle)
		cost += bsc->cost; // full price

	if ((haircolor != newhaircolor) && (hairstyle == newhairstyle))
		cost += bsc->cost * 0.5f; // +1/2 of price

	if (facialhair != newfacialhair)
		cost += bsc->cost * 0.75f; // +3/4 of price

	if (newSkin && skincolor != newSkin->hair_id)
		cost += bsc->cost * 0.75f; // +5/6 of price

	return uint32(cost);
}

void Player::InitGlyphsForLevel() {
	uint8 level = getLevel();
	if (level < 25)
		return;

	uint32 glyphMask = 0;
	uint8 order = level / 25 - 1;
	uint32 counter = 0;

	for (uint32 i = 0; i < sGlyphSlotStore.GetNumRows(); ++i)
		if (GlyphSlotEntry const * gs = sGlyphSlotStore.LookupEntry(i)) {
			if (gs->LearningOrder <= order)
				glyphMask |= (1 << uint8(counter)); // Set the appropriate bit

			SetGlyphSlot(counter++, gs->Id);
		}

	SetUInt32Value(PLAYER_GLYPHS_ENABLED, glyphMask);

	// This enables glyph removal and reagent checking.
	// If the player doesn't know either of these, BlizzardUI will Lua error him to death :P
	// TODO: Move this to DB
	if (level >= 81 && !HasSpell(90647))
		learnSpell(90647, false);
	else if (!HasSpell(89964))
		learnSpell(89964, false);
}

bool Player::isTotalImmune() {
	AuraEffectList const& immune = GetAuraEffectsByType(
			SPELL_AURA_SCHOOL_IMMUNITY);

	uint32 immuneMask = 0;
	for (AuraEffectList::const_iterator itr = immune.begin();
			itr != immune.end(); ++itr) {
		immuneMask |= (*itr)->GetMiscValue();
		if (immuneMask & SPELL_SCHOOL_MASK_ALL) // total immunity
			return true;
	}
	return false;
}

bool Player::HasTitle(uint32 bitIndex) {
	if (bitIndex > MAX_TITLE_INDEX)
		return false;

	uint32 fieldIndexOffset = bitIndex / 32;
	uint32 flag = 1 << (bitIndex % 32);
	return HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag);
}

void Player::SetTitle(CharTitlesEntry const* title, bool lost) {
	uint32 fieldIndexOffset = title->bit_index / 32;
	uint32 flag = 1 << (title->bit_index % 32);

	if (lost) {
		if (!HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag))
			return;

		RemoveFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag);
	} else {
		if (HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag))
			return;

		SetFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag);
	}

	WorldPacket data(SMSG_TITLE_EARNED, 4 + 4);
	data << uint32(title->bit_index);
	data << uint32(lost ? 0 : 1); // 1 - earned, 0 - lost
	GetSession()->SendPacket(&data);
}

/*-----------------------ARKCORE--------------------------*/
bool Player::isTotalImmunity() {
	AuraEffectList const& immune = GetAuraEffectsByType(
			SPELL_AURA_SCHOOL_IMMUNITY);

	for (AuraEffectList::const_iterator itr = immune.begin();
			itr != immune.end(); ++itr) {
		if (((*itr)->GetMiscValue() & SPELL_SCHOOL_MASK_ALL) != 0) // total immunity
				{
			return true;
		}
		if (((*itr)->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) != 0) // physical damage immunity
				{
			for (AuraEffectList::const_iterator i = immune.begin();
					i != immune.end(); ++i) {
				if (((*i)->GetMiscValue() & SPELL_SCHOOL_MASK_MAGIC) != 0) // magic immunity
						{
					return true;
				}
			}
		}
	}
	return false;
}

void Player::UpdateCharmedAI() {
	//This should only called in Player::Update
	Creature *charmer = GetCharmer()->ToCreature();

	//kill self if charm aura has infinite duration
	if (charmer->IsInEvadeMode()) {
		AuraEffectList const& auras = GetAuraEffectsByType(
				SPELL_AURA_MOD_CHARM);
		for (AuraEffectList::const_iterator iter = auras.begin();
				iter != auras.end(); ++iter)
			if ((*iter)->GetCasterGUID() == charmer->GetGUID()
					&& (*iter)->GetBase()->IsPermanent()) {
				charmer->DealDamage(this, GetHealth(), NULL, DIRECT_DAMAGE,
						SPELL_SCHOOL_MASK_NORMAL, NULL, false);
				return;
			}
	}

	if (!charmer->isInCombat())
		GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST,
				PET_FOLLOW_ANGLE);

	Unit *target = getVictim();
	if (!target || !charmer->canAttack(target)) {
		target = charmer->SelectNearestTarget();
		if (!target)
			return;

		GetMotionMaster()->MoveChase(target);
		Attack(target, true);
	}
}

void Player::AddGlobalCooldown(SpellEntry const *spellInfo, Spell *spell) {
	if (!spellInfo || !spellInfo->StartRecoveryTime)
		return;

	float cdTime = float(spellInfo->StartRecoveryTime);

	if (!(spellInfo->Attributes & (SPELL_ATTR0_UNK4 | SPELL_ATTR0_PASSIVE)))
		cdTime *= GetFloatValue(UNIT_MOD_CAST_SPEED);
	else if (IsRangedWeaponSpell(spellInfo) && !spell->IsAutoRepeat())
		cdTime *= m_modAttackSpeedPct[RANGED_ATTACK];

	if (cdTime > 1500.0f)
		cdTime = 1500.0f;

	ApplySpellMod(spellInfo->Id, SPELLMOD_GLOBAL_COOLDOWN, cdTime, spell);
	if (cdTime > 0)
		m_globalCooldowns[spellInfo->StartRecoveryCategory] = uint32(cdTime);
}

bool Player::HasGlobalCooldown(SpellEntry const *spellInfo) const {
	if (!spellInfo)
		return false;

	std::map<uint32, uint32>::const_iterator itr = m_globalCooldowns.find(
			spellInfo->StartRecoveryCategory);
	return itr != m_globalCooldowns.end()
			&& (itr->second > sWorld->GetUpdateTime());
}

void Player::RemoveGlobalCooldown(SpellEntry const *spellInfo) {
	if (!spellInfo || !spellInfo->StartRecoveryTime)
		return;

	m_globalCooldowns[spellInfo->StartRecoveryCategory] = 0;
}

uint32 Player::GetRuneBaseCooldown(uint8 index) {
	uint8 rune = GetBaseRune(index);
	uint32 cooldown = RUNE_BASE_COOLDOWN;

	AuraEffectList const& regenAura = GetAuraEffectsByType(
			SPELL_AURA_MOD_POWER_REGEN_PERCENT);
	for (AuraEffectList::const_iterator i = regenAura.begin();
			i != regenAura.end(); ++i) {
		if ((*i)->GetMiscValue() == POWER_RUNE && (*i)->GetMiscValueB() == rune)
			cooldown = cooldown * (100 - (*i)->GetAmount()) / 100;
	}

	return cooldown;
}

void Player::RemoveRunesByAuraEffect(AuraEffect const * aura) {
	for (uint8 i = 0; i < MAX_RUNES; ++i) {
		if (m_runes->runes[i].ConvertAura == aura) {
			ConvertRune(i, GetBaseRune(i));
			SetRuneConvertAura(i, NULL);
		}
	}
}

void Player::RestoreBaseRune(uint8 index) {
	AuraEffect const * aura = m_runes->runes[index].ConvertAura;
	ConvertRune(index, GetBaseRune(index));
	SetRuneConvertAura(index, NULL);
	// Don't drop passive talents providing rune convertion
	if (!aura || aura->GetAuraType() != SPELL_AURA_CONVERT_RUNE)
		return;
	for (uint8 i = 0; i < MAX_RUNES; ++i) {
		if (aura == m_runes->runes[i].ConvertAura)
			return;
	}
	aura->GetBase()->Remove();
}

void Player::ConvertRune(uint8 index, RuneType newType) {
	SetCurrentRune(index, newType);

	WorldPacket data(SMSG_CONVERT_RUNE, 2);
	data << uint8(index);
	data << uint8(newType);
	GetSession()->SendPacket(&data);
}

void Player::ResyncRunes(uint8 count) {
	WorldPacket data(SMSG_RESYNC_RUNES, 4 + count * 2);
	data << uint32(count);
	for (uint32 i = 0; i < count; ++i) {
		data << uint8(GetCurrentRune(i)); // rune type
		data << uint8(255 - (GetRuneCooldown(i) * 51)); // passed cooldown time (0-255)
	}
	GetSession()->SendPacket(&data);
}

void Player::AddRunePower(uint8 index) {
	WorldPacket data(SMSG_ADD_RUNE_POWER, 4);
	data << uint32(1 << index); // mask (0x00-0x3F probably)
	GetSession()->SendPacket(&data);
}

static RuneType runeSlotTypes[MAX_RUNES] = {
/*0*/RUNE_BLOOD,
/*1*/RUNE_BLOOD,
/*2*/RUNE_UNHOLY,
/*3*/RUNE_UNHOLY,
/*4*/RUNE_FROST,
/*5*/RUNE_FROST };

void Player::InitRunes() {
	if (getClass() != CLASS_DEATH_KNIGHT)
		return;

	m_runes = new Runes;

	m_runes->runeState = 0;
	m_runes->lastUsedRune = RUNE_BLOOD;

	for (uint32 i = 0; i < MAX_RUNES; ++i) {
		SetBaseRune(i, runeSlotTypes[i]); // init base types
		SetCurrentRune(i, runeSlotTypes[i]); // init current types
		SetRuneCooldown(i, 0); // reset cooldowns
		SetRuneConvertAura(i, NULL);
		m_runes->SetRuneState(i);
	}

	for (uint32 i = 0; i < NUM_RUNE_TYPES; ++i)
		SetFloatValue(PLAYER_RUNE_REGEN_1 + i, 0.1f);
}

bool Player::IsBaseRuneSlotsOnCooldown(RuneType runeType) const {
	for (uint32 i = 0; i < MAX_RUNES; ++i)
		if (GetBaseRune(i) == runeType && GetRuneCooldown(i) == 0)
			return false;

	return true;
}

void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id,
		LootStore const& store, bool broadcast) {
	Loot loot;
	loot.FillLoot(loot_id, store, this, true);

	uint32 max_slot = loot.GetMaxSlotInLootFor(this);
	for (uint32 i = 0; i < max_slot; ++i) {
		LootItem* lootItem = loot.LootItemInSlot(i, this);

		ItemPosCountVec dest;
		uint8 msg = CanStoreNewItem(bag, slot, dest, lootItem->itemid,
				lootItem->count);
		if (msg != EQUIP_ERR_OK && slot != NULL_SLOT)
			msg = CanStoreNewItem(bag, NULL_SLOT, dest, lootItem->itemid,
					lootItem->count);
		if (msg != EQUIP_ERR_OK && bag != NULL_BAG)
			msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, lootItem->itemid,
					lootItem->count);
		if (msg != EQUIP_ERR_OK) {
			SendEquipError(msg, NULL, NULL, lootItem->itemid);
			continue;
		}

		Item* pItem = StoreNewItem(dest, lootItem->itemid, true,
				lootItem->randomPropertyId);
		SendNewItem(pItem, lootItem->count, false, false, broadcast);
	}
}

void Player::StoreLootItem(uint8 lootSlot, Loot* loot) {
	QuestItem *qitem = NULL;
	QuestItem *ffaitem = NULL;
	QuestItem *conditem = NULL;

	LootItem *item = loot->LootItemInSlot(lootSlot, this, &qitem, &ffaitem,
			&conditem);

	if (!item) {
		SendEquipError(EQUIP_ERR_ALREADY_LOOTED, NULL, NULL);
		return;
	}

	// questitems use the blocked field for other purposes
	if (!qitem && item->is_blocked) {
		SendLootRelease(GetLootGUID());
		return;
	}

	ItemPosCountVec dest;
	uint8 msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item->itemid,
			item->count);
	if (msg == EQUIP_ERR_OK) {
		AllowedLooterSet* looters = item->GetAllowedLooters();
		Item * newitem = StoreNewItem(dest, item->itemid, true,
				item->randomPropertyId, (looters->size() > 1) ? looters : NULL);

		if (qitem) {
			qitem->is_looted = true;
			//freeforall is 1 if everyone's supposed to get the quest item.
			if (item->freeforall || loot->GetPlayerQuestItems().size() == 1)
				SendNotifyLootItemRemoved(lootSlot);
			else
				loot->NotifyQuestItemRemoved(qitem->index);
		} else {
			if (ffaitem) {
				//freeforall case, notify only one player of the removal
				ffaitem->is_looted = true;
				SendNotifyLootItemRemoved(lootSlot);
			} else {
				//not freeforall, notify everyone
				if (conditem)
					conditem->is_looted = true;
				loot->NotifyItemRemoved(lootSlot);
			}
		}

		//if only one person is supposed to loot the item, then set it to looted
		if (!item->freeforall)
			item->is_looted = true;

		--loot->unlootedCount;

		SendNewItem(newitem, uint32(item->count), false, false, true);
		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count);
		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, loot->loot_type,
				item->count);
		GetAchievementMgr().UpdateAchievementCriteria(
				ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item->itemid,
				item->count);
	} else
		SendEquipError(msg, NULL, NULL, item->itemid);
}

uint32 Player::CalculateTalentsPoints() const {
	uint32 base_talent = 0;
	if (getLevel() >= 10 && getLevel() <= 81)
		base_talent = (((getLevel() - 10 + 1)
				- (((getLevel() - 10 + 1) % 2) == 1 ? 1 : 0)) / 2) + 1;
	else
		base_talent = getLevel() - 44;

	if (getClass() != CLASS_DEATH_KNIGHT || GetMapId() != 609)
		return uint32(base_talent * sWorld->getRate(RATE_TALENT));

	uint32 talentPointsForLevel = getLevel() < 56 ? 0 : getLevel() - 55;
	talentPointsForLevel += m_questRewardTalentCount;

	if (talentPointsForLevel > base_talent)
		talentPointsForLevel = base_talent;

	return uint32(talentPointsForLevel * sWorld->getRate(RATE_TALENT));
}

bool Player::IsKnowHowFlyIn(uint32 mapid, uint32 zone) const {
	// continent checked in SpellMgr::GetSpellAllowedInLocationError at cast and area update
	uint32 v_map = GetVirtualMapForMapAndZone(mapid, zone);
	return (v_map != 571 || HasSpell(54197)) && (v_map != 0 || HasSpell(90267)); // Cold Weather Flying
}

void Player::learnSpellHighRank(uint32 spellid) {
	learnSpell(spellid, false);

	if (uint32 next = sSpellMgr->GetNextSpellInChain(spellid))
		learnSpellHighRank(next);
}

void Player::_LoadSkills(PreparedQueryResult result) {
	//                                                           0      1      2
	// SetPQuery(PLAYER_LOGIN_QUERY_LOADSKILLS,          "SELECT skill, value, max FROM character_skills WHERE guid = '%u'", GUID_LOPART(m_guid));

	uint32 count = 0;
	if (result) {
		do {
			Field* fields = result->Fetch();
			uint16 skill = fields[0].GetUInt16();
			uint16 value = fields[1].GetUInt16();
			uint16 max = fields[2].GetUInt16();

			SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(skill);
			if (!pSkill) {
				sLog->outError("Character %u has skill %u that does not exist.",
						GetGUIDLow(), skill);
				continue;
			}

			// set fixed skill ranges
			switch (GetSkillRangeType(pSkill, false)) {
			case SKILL_RANGE_LANGUAGE: // 300..300
				value = max = 300;
				break;
			case SKILL_RANGE_MONO: // 1..1, grey monolite bar
				value = max = 1;
				break;
			default:
				break;
			}
			if (value == 0) {
				sLog->outError(
						"Character %u has skill %u with value 0. Will be deleted.",
						GetGUIDLow(), skill);
				CharacterDatabase.PExecute(
						"DELETE FROM character_skills WHERE guid = '%u' AND skill = '%u' ",
						GetGUIDLow(), skill);
				continue;
			}

			// enable unlearn button for primary professions only
			if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION)
				SetUInt32Value(PLAYER_SKILL_INDEX(count),
						MAKE_PAIR32(skill, 1));
			else
				SetUInt32Value(PLAYER_SKILL_INDEX(count),
						MAKE_PAIR32(skill, 0));

			SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count),
					MAKE_SKILL_VALUE(value, max));
			SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count), 0);

			mSkillStatus.insert(
					SkillStatusMap::value_type(skill,
							SkillStatusData(count, SKILL_UNCHANGED)));

			learnSkillRewardedSpells(skill, value);

			++count;

			if (count >= PLAYER_MAX_SKILLS) // client limit
			{
				sLog->outError("Character %u has more than %u skills.",
						GetGUIDLow(), PLAYER_MAX_SKILLS);
				break;
			}
		} while (result->NextRow());
	}

	for (; count < PLAYER_MAX_SKILLS; ++count) {
		SetUInt32Value(PLAYER_SKILL_INDEX(count), 0);
		SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(count), 0);
		SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(count), 0);
	}

	// special settings
	if (getClass() == CLASS_DEATH_KNIGHT) {
		uint8 base_level = std::min(getLevel(),
				uint8(sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL)));
		if (base_level < 1)
			base_level = 1;
		uint16 base_skill = (base_level - 1) * 5; // 270 at starting level 55
		if (base_skill < 1)
			base_skill = 1; // skill mast be known and then > 0 in any case

		if (GetPureSkillValue(SKILL_FIRST_AID) < base_skill)
			SetSkill(SKILL_FIRST_AID, 0, base_skill, base_skill);
		if (GetPureSkillValue(SKILL_AXES) < base_skill)
			SetSkill(SKILL_AXES, 0, base_skill, base_skill);
		if (GetPureSkillValue(SKILL_DEFENSE) < base_skill)
			SetSkill(SKILL_DEFENSE, 0, base_skill, base_skill);
		if (GetPureSkillValue(SKILL_POLEARMS) < base_skill)
			SetSkill(SKILL_POLEARMS, 0, base_skill, base_skill);
		if (GetPureSkillValue(SKILL_SWORDS) < base_skill)
			SetSkill(SKILL_SWORDS, 0, base_skill, base_skill);
		if (GetPureSkillValue(SKILL_2H_AXES) < base_skill)
			SetSkill(SKILL_2H_AXES, 0, base_skill, base_skill);
		if (GetPureSkillValue(SKILL_2H_SWORDS) < base_skill)
			SetSkill(SKILL_2H_SWORDS, 0, base_skill, base_skill);
		if (GetPureSkillValue(SKILL_UNARMED) < base_skill)
			SetSkill(SKILL_UNARMED, 0, base_skill, base_skill);
	}
}

uint32 Player::GetPhaseMaskForSpawn() const {
	uint32 phase = PHASEMASK_NORMAL;
	if (!isGameMaster())
		phase = GetPhaseMask();
	else {
		AuraEffectList const& phases = GetAuraEffectsByType(SPELL_AURA_PHASE);
		if (!phases.empty())
			phase = phases.front()->GetMiscValue();
	}

	// some aura phases include 1 normal map in addition to phase itself
	if (uint32 n_phase = phase & ~PHASEMASK_NORMAL)
		return n_phase;

	return PHASEMASK_NORMAL;
}

uint8 Player::CanEquipUniqueItem(Item* pItem, uint8 eslot,
		uint32 limit_count) const {
	ItemPrototype const* pProto = pItem->GetProto();

	// proto based limitations
	if (uint8 res = CanEquipUniqueItem(pProto, eslot, limit_count))
		return res;

	// check unique-equipped on gems
	for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT;
			enchant_slot < SOCK_ENCHANTMENT_SLOT + 3; ++enchant_slot) {
		uint32 enchant_id = pItem->GetEnchantmentId(
				EnchantmentSlot(enchant_slot));
		if (!enchant_id)
			continue;
		SpellItemEnchantmentEntry const* enchantEntry =
				sSpellItemEnchantmentStore.LookupEntry(enchant_id);
		if (!enchantEntry)
			continue;

		ItemPrototype const* pGem = ObjectMgr::GetItemPrototype(
				enchantEntry->GemID);
		if (!pGem)
			continue;

		// include for check equip another gems with same limit category for not equipped item (and then not counted)
		uint32 gem_limit_count =
				!pItem->IsEquipped() && pGem->ItemLimitCategory ?
						pItem->GetGemCountWithLimitCategory(
								pGem->ItemLimitCategory) :
						1;

		if (uint8 res = CanEquipUniqueItem(pGem, eslot, gem_limit_count))
			return res;
	}

	return EQUIP_ERR_OK;
}

uint8 Player::CanEquipUniqueItem(ItemPrototype const* itemProto,
		uint8 except_slot, uint32 limit_count) const {
	// check unique-equipped on item
	if (itemProto->Flags & ITEM_PROTO_FLAG_UNIQUE_EQUIPPED) {
		// there is an equip limit on this item
		if (HasItemOrGemWithIdEquipped(itemProto->ItemId, 1, except_slot))
			return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE;
	}

	// check unique-equipped limit
	if (itemProto->ItemLimitCategory) {
		ItemLimitCategoryEntry const* limitEntry =
				sItemLimitCategoryStore.LookupEntry(
						itemProto->ItemLimitCategory);
		if (!limitEntry)
			return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;

		// NOTE: limitEntry->mode not checked because if item have have-limit then it applied and to equip case

		if (limit_count > limitEntry->maxCount)
			return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED;

		// there is an equip limit on this item
		if (HasItemOrGemWithLimitCategoryEquipped(itemProto->ItemLimitCategory,
				limitEntry->maxCount - limit_count + 1, except_slot))
			return EQUIP_ERR_ITEM_MAX_COUNT_EQUIPPED_SOCKETED;
	}

	return EQUIP_ERR_OK;
}

void Player::HandleFall(MovementInfo const& movementInfo) {
	// calculate total z distance of the fall
	float z_diff = m_lastFallZ - movementInfo.pos.GetPositionZ();
	//sLog->outDebug("zDiff = %f", z_diff);

	//Players with low fall distance, Feather Fall or physical immunity (charges used) are ignored
	// 14.57 can be calculated by resolving damageperc formula below to 0
	if (z_diff >= 14.57f && !isDead() && !isGameMaster()
			&& !HasAuraType(SPELL_AURA_HOVER)
			&& !HasAuraType(SPELL_AURA_FEATHER_FALL)
			&& !HasAuraType(SPELL_AURA_FLY)
			&& !IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL)) {
		//Safe fall, fall height reduction
		int32 safe_fall = GetTotalAuraModifier(SPELL_AURA_SAFE_FALL);

		float damageperc = 0.018f * (z_diff - safe_fall) - 0.2426f;

		if (damageperc > 0) {
			uint32 damage = (uint32) (damageperc * GetMaxHealth()
					* sWorld->getRate(RATE_DAMAGE_FALL));

			float height = movementInfo.pos.m_positionZ;
			UpdateGroundPositionZ(movementInfo.pos.m_positionX,
					movementInfo.pos.m_positionY, height);

			if (damage > 0) {
				//Prevent fall damage from being more than the player maximum health
				if (damage > GetMaxHealth())
					damage = GetMaxHealth();

				// Gust of Wind
				if (HasAura(43621))
					damage = GetMaxHealth() / 2;

				uint32 original_health = GetHealth();
				uint32 final_damage = EnvironmentalDamage(DAMAGE_FALL, damage);

				// recheck alive, might have died of EnvironmentalDamage, avoid cases when player die in fact like Spirit of Redemption case
				if (isAlive() && final_damage < original_health)
					GetAchievementMgr().UpdateAchievementCriteria(
							ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING,
							uint32(z_diff * 100));
			}

			//Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction
			sLog->outStaticDebug(
					"FALLDAMAGE z=%f sz=%f pZ=%f FallTime=%d mZ=%f damage=%d SF=%d",
					movementInfo.pos.GetPositionZ(), height, GetPositionZ(),
					movementInfo.fallTime, height, damage, safe_fall);
		}
	}
	RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_LANDING); // No fly zone - Parachute
}

void Player::UpdateAchievementCriteria(AchievementCriteriaTypes type,
		uint32 miscvalue1/*=0*/, uint32 miscvalue2/*=0*/, Unit *unit/*=NULL*/,
		uint32 time/*=0*/) {
	GetAchievementMgr().UpdateAchievementCriteria(type, miscvalue1, miscvalue2,
			unit, time);
}

void Player::CompletedAchievement(AchievementEntry const* entry,
		bool ignoreGMAllowAchievementConfig) {
	GetAchievementMgr().CompletedAchievement(entry,
			ignoreGMAllowAchievementConfig);
}

bool Player::HasAchieved(uint32 entry) {
	if (AchievementEntry const *achievement = sAchievementStore.LookupEntry(entry))
		return GetAchievementMgr().HasAchieved(achievement);
	return false;
}

void Player::LearnTalent(uint32 talentId, uint32 talentRank, bool one) {
	uint32 CurTalentPoints = GetFreeTalentPoints();

	if (CurTalentPoints == 0)
		return;

	if (talentRank >= MAX_TALENT_RANK)
		return;

	TalentEntry const *talentInfo = sTalentStore.LookupEntry(talentId);

	if (!talentInfo)
		return;

	TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(
			talentInfo->TalentTab);

	if (!talentTabInfo)
		return;

	if (one
			&& talentTabInfo->TalentTabID != GetTalentBranchSpec(m_activeSpec)) {
		uint32 pointInBranchSpec = 0;
		for (PlayerTalentMap::iterator itr = m_talents[m_activeSpec]->begin();
				itr != m_talents[m_activeSpec]->end(); itr++) {
			for (uint32 i = 0; i < sTalentStore.GetNumRows(); i++) {
				const TalentEntry * thisTalent = sTalentStore.LookupEntry(i);
				if (thisTalent) {
					int thisrank = -1;
					for (int j = 0; j < 5; j++)
						if (thisTalent->RankID[j] == itr->first) {
							thisrank = j;
							break;
						}
					if (thisrank != -1) {
						if (thisTalent->TalentTab
								== GetTalentBranchSpec(m_activeSpec)) {
							int8 curtalent_maxrank = -1;
							for (int8 rank = MAX_TALENT_RANK - 1; rank >= 0;
									--rank) {
								if (thisTalent->RankID[rank]
										&& HasTalent(thisTalent->RankID[rank],
												m_activeSpec)) {
									curtalent_maxrank = rank;
									break;
								}
							}
							if (curtalent_maxrank != -1
									&& thisrank == curtalent_maxrank)
								pointInBranchSpec += curtalent_maxrank + 1;
						}
					}
				}
			}
		}
		if (pointInBranchSpec < 31)
			return;
	}

	// prevent learn talent for different class (cheating)
	if ((getClassMask() & talentTabInfo->ClassMask) == 0)
		return;

	// find current max talent rank (0~5)
	uint8 curtalent_maxrank = 0; // 0 = not learned any rank
	for (int8 rank = MAX_TALENT_RANK - 1; rank >= 0; --rank) {
		if (talentInfo->RankID[rank] && HasSpell(talentInfo->RankID[rank])) {
			curtalent_maxrank = (rank + 1);
			break;
		}
	}

	// we already have same or higher talent rank learned
	if (curtalent_maxrank >= (talentRank + 1))
		return;

	// check if we have enough talent points
	if (CurTalentPoints < (talentRank - curtalent_maxrank + 1))
		return;

	// Check if it requires another talent
	if (talentInfo->DependsOn > 0) {
		if (TalentEntry const *depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn)) {
			bool hasEnoughRank = false;
			for (uint8 rank = talentInfo->DependsOnRank; rank < MAX_TALENT_RANK;
					rank++) {
				if (depTalentInfo->RankID[rank] != 0)
					if (HasSpell(depTalentInfo->RankID[rank]))
						hasEnoughRank = true;
			}
			if (!hasEnoughRank)
				return;
		}
	}

	// Find out how many points we have in this field
	uint32 spentPoints = 0;

	uint32 tTab = talentInfo->TalentTab;
	if (talentInfo->Row > 0) {
		uint32 numRows = sTalentStore.GetNumRows();
		for (uint32 i = 0; i < numRows; i++) // Loop through all talents.
				{
			// Someday, someone needs to revamp
			const TalentEntry *tmpTalent = sTalentStore.LookupEntry(i);
			if (tmpTalent) // the way talents are tracked
			{
				if (tmpTalent->TalentTab == tTab) {
					for (uint8 rank = 0; rank < MAX_TALENT_RANK; rank++) {
						if (tmpTalent->RankID[rank] != 0) {
							if (HasSpell(tmpTalent->RankID[rank])) {
								spentPoints += (rank + 1);
							}
						}
					}
				}
			}
		}
	}

	// not have required min points spent in talent tree
	if (spentPoints < (talentInfo->Row * MAX_TALENT_RANK))
		return;

	// spell not set in talent.dbc
	uint32 spellid = talentInfo->RankID[talentRank];
	if (spellid == 0) {
		sLog->outError("Talent.dbc have for talent: %u Rank: %u spell id = 0",
				talentId, talentRank);
		return;
	}

	// already known
	if (HasSpell(spellid))
		return;

	// learn! (other talent ranks will unlearned at learning)
	learnSpell(spellid, false);
	AddTalent(spellid, m_activeSpec, true);

	sLog->outDetail("TalentID: %u Rank: %u Spell: %u Spec: %u\n", talentId,
			talentRank, spellid, m_activeSpec);

	// update free talent points
	SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1));
}

void Player::LearnPetTalent(uint64 petGuid, uint32 talentId,
		uint32 talentRank) {
	Pet *pet = GetPet();

	if (!pet)
		return;

	if (petGuid != pet->GetGUID())
		return;

	uint32 CurTalentPoints = pet->GetFreeTalentPoints();

	if (CurTalentPoints == 0)
		return;

	if (talentRank >= MAX_PET_TALENT_RANK)
		return;

	TalentEntry const *talentInfo = sTalentStore.LookupEntry(talentId);

	if (!talentInfo)
		return;

	TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(
			talentInfo->TalentTab);

	if (!talentTabInfo)
		return;

	CreatureInfo const *ci = pet->GetCreatureInfo();

	if (!ci)
		return;

	CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(
			ci->family);

	if (!pet_family)
		return;

	if (pet_family->petTalentType == PET_TALENT_TYPE_NOT_HUNTER_PET) // not hunter pet
		return;

	// prevent learn talent for different family (cheating)
	if (!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask))
		return;

	// find current max talent rank (0~5)
	uint8 curtalent_maxrank = 0; // 0 = not learned any rank
	for (int8 rank = MAX_TALENT_RANK - 1; rank >= 0; --rank) {
		if (talentInfo->RankID[rank]
				&& pet->HasSpell(talentInfo->RankID[rank])) {
			curtalent_maxrank = (rank + 1);
			break;
		}
	}

	// we already have same or higher talent rank learned
	if (curtalent_maxrank >= (talentRank + 1))
		return;

	// check if we have enough talent points
	if (CurTalentPoints < (talentRank - curtalent_maxrank + 1))
		return;

	// Check if it requires another talent
	if (talentInfo->DependsOn > 0) {
		if (TalentEntry const *depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn)) {
			bool hasEnoughRank = false;
			for (uint8 rank = talentInfo->DependsOnRank; rank < MAX_TALENT_RANK;
					rank++) {
				if (depTalentInfo->RankID[rank] != 0)
					if (pet->HasSpell(depTalentInfo->RankID[rank]))
						hasEnoughRank = true;
			}
			if (!hasEnoughRank)
				return;
		}
	}

	// Find out how many points we have in this field
	uint32 spentPoints = 0;

	uint32 tTab = talentInfo->TalentTab;
	if (talentInfo->Row > 0) {
		uint32 numRows = sTalentStore.GetNumRows();
		for (uint32 i = 0; i < numRows; ++i) // Loop through all talents.
				{
			// Someday, someone needs to revamp
			const TalentEntry *tmpTalent = sTalentStore.LookupEntry(i);
			if (tmpTalent) // the way talents are tracked
			{
				if (tmpTalent->TalentTab == tTab) {
					for (uint8 rank = 0; rank < MAX_TALENT_RANK; rank++) {
						if (tmpTalent->RankID[rank] != 0) {
							if (pet->HasSpell(tmpTalent->RankID[rank])) {
								spentPoints += (rank + 1);
							}
						}
					}
				}
			}
		}
	}

	// not have required min points spent in talent tree
	if (spentPoints < (talentInfo->Row * MAX_PET_TALENT_RANK))
		return;

	// spell not set in talent.dbc
	uint32 spellid = talentInfo->RankID[talentRank];
	if (spellid == 0) {
		sLog->outError("Talent.dbc have for talent: %u Rank: %u spell id = 0",
				talentId, talentRank);
		return;
	}

	// already known
	if (pet->HasSpell(spellid))
		return;

	// learn! (other talent ranks will unlearned at learning)
	pet->learnSpell(spellid);
	sLog->outDetail("PetTalentID: %u Rank: %u Spell: %u\n", talentId,
			talentRank, spellid);

	// update free talent points
	pet->SetFreeTalentPoints(
			CurTalentPoints - (talentRank - curtalent_maxrank + 1));
}

void Player::UpdateFallInformationIfNeed(MovementInfo const& minfo,
		uint32 opcode) {
	if (m_lastFallTime >= minfo.fallTime
			|| m_lastFallZ <= minfo.pos.GetPositionZ()
			|| opcode == MSG_MOVE_FALL_LAND)
		SetFallInformation(minfo.fallTime, minfo.pos.GetPositionZ());
}

void Player::UnsummonPetTemporaryIfAny() {
	Pet* pet = GetPet();
	if (!pet)
		return;

	if (!m_temporaryUnsummonedPetNumber && pet->isControlled()
			&& !pet->isTemporarySummoned()) {
		m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber();
		m_oldpetspell = pet->GetUInt32Value(UNIT_CREATED_BY_SPELL);
	}

	RemovePet(pet, PET_SLOT_ACTUAL_PET_SLOT);
}

void Player::ResummonPetTemporaryUnSummonedIfAny() {
	if (!m_temporaryUnsummonedPetNumber)
		return;

	// not resummon in not appropriate state
	if (IsPetNeedBeTemporaryUnsummoned())
		return;

	if (GetPetGUID())
		return;

	Pet* NewPet = new Pet(this);
	if (!NewPet->LoadPetFromDB(this, 0, m_temporaryUnsummonedPetNumber, true))
		delete NewPet;

	m_temporaryUnsummonedPetNumber = 0;
}

bool Player::canSeeSpellClickOn(Creature const *c) const {
	if (!c->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK))
		return false;

	SpellClickInfoMapBounds clickPair = sObjectMgr->GetSpellClickInfoMapBounds(
			c->GetEntry());
	if (clickPair.first == clickPair.second)
		return true;

	for (SpellClickInfoMap::const_iterator itr = clickPair.first;
			itr != clickPair.second; ++itr)
		if (itr->second.IsFitToRequirements(this, c))
			return true;

	return false;
}

void Player::BuildPlayerTalentsInfoData(WorldPacket *data) {
	*data << uint32(GetFreeTalentPoints()); // unspentTalentPoints
	*data << uint8(m_specsCount); // talent group count (0, 1 or 2)
	*data << uint8(m_activeSpec); // talent group index (0 or 1)

	if (m_specsCount) {
		if (m_specsCount > MAX_TALENT_SPECS)
			m_specsCount = MAX_TALENT_SPECS;

		// loop through all specs (only 1 for now)
		for (uint32 specIdx = 0; specIdx < m_specsCount; ++specIdx) {
			uint8 talentIdCount = 0;
			*data << uint32(GetTalentBranchSpec(specIdx)); //branchSpec
			size_t pos = data->wpos();
			*data << uint8(talentIdCount); // [PH], talentIdCount

			// find class talent tabs (all players have 3 talent tabs)
			uint32 const* talentTabIds = GetTalentTabPages(getClass());

			for (uint8 i = 0; i < MAX_TALENT_TABS; ++i) {
				uint32 talentTabId = talentTabIds[i];

				for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows();
						++talentId) {
					TalentEntry const* talentInfo = sTalentStore.LookupEntry(
							talentId);
					if (!talentInfo)
						continue;

					// skip another tab talents
					if (talentInfo->TalentTab != talentTabId)
						continue;

					// find max talent rank (0~4)
					int8 curtalent_maxrank = -1;
					for (int8 rank = MAX_TALENT_RANK - 1; rank >= 0; --rank) {
						if (talentInfo->RankID[rank]
								&& HasTalent(talentInfo->RankID[rank], specIdx)) {
							curtalent_maxrank = rank;
							break;
						}
					}

					// not learned talent
					if (curtalent_maxrank < 0)
						continue;

					*data << uint32(talentInfo->TalentID); // Talent.dbc
					*data << uint8(curtalent_maxrank); // talentMaxRank (0-4)

					++talentIdCount;
				}
			}

			data->put<uint8>(pos, talentIdCount); // put real count

			*data << uint8(MAX_GLYPH_SLOT_INDEX); // glyphs count

			for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
				*data << uint16(m_Glyphs[specIdx][i]); // GlyphProperties.dbc
		}
	}
}

void Player::BuildPetTalentsInfoData(WorldPacket *data) {
	uint32 unspentTalentPoints = 0;
	size_t pointsPos = data->wpos();
	*data << uint32(unspentTalentPoints); // [PH], unspentTalentPoints

	uint8 talentIdCount = 0;
	size_t countPos = data->wpos();
	*data << uint8(talentIdCount); // [PH], talentIdCount

	Pet *pet = GetPet();
	if (!pet)
		return;

	unspentTalentPoints = pet->GetFreeTalentPoints();

	data->put<uint32>(pointsPos, unspentTalentPoints); // put real points

	CreatureInfo const *ci = pet->GetCreatureInfo();
	if (!ci)
		return;

	CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(
			ci->family);
	if (!pet_family
			|| pet_family->petTalentType == PET_TALENT_TYPE_NOT_HUNTER_PET)
		return;

	for (uint32 talentTabId = 1; talentTabId < sTalentTabStore.GetNumRows();
			++talentTabId) {
		TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(
				talentTabId);
		if (!talentTabInfo)
			continue;

		if (!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask))
			continue;

		for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows();
				++talentId) {
			TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentId);
			if (!talentInfo)
				continue;

			// skip another tab talents
			if (talentInfo->TalentTab != talentTabId)
				continue;

			// find max talent rank (0~4)
			int8 curtalent_maxrank = -1;
			for (int8 rank = MAX_TALENT_RANK - 1; rank >= 0; --rank) {
				if (talentInfo->RankID[rank]
						&& pet->HasSpell(talentInfo->RankID[rank])) {
					curtalent_maxrank = rank;
					break;
				}
			}

			// not learned talent
			if (curtalent_maxrank < 0)
				continue;

			*data << uint32(talentInfo->TalentID); // Talent.dbc
			*data << uint8(curtalent_maxrank); // talentMaxRank (0-4)

			++talentIdCount;
		}

		data->put<uint8>(countPos, talentIdCount); // put real count

		break;
	}
}

void Player::SendTalentsInfoData(bool pet) {
	WorldPacket data(SMSG_TALENTS_INFO, 50);
	data << uint8(pet ? 1 : 0);
	if (pet)
		BuildPetTalentsInfoData(&data);
	else
		BuildPlayerTalentsInfoData(&data);
	GetSession()->SendPacket(&data);
}

void Player::BuildEnchantmentsInfoData(WorldPacket *data) {
	uint32 slotUsedMask = 0;
	size_t slotUsedMaskPos = data->wpos();
	*data << uint32(slotUsedMask); // slotUsedMask < 0x80000

	for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i) {
		Item *item = GetItemByPos(INVENTORY_SLOT_BAG_0, i);

		if (!item)
			continue;

		slotUsedMask |= (1 << i);

		*data << uint32(item->GetEntry()); // item entry

		uint16 enchantmentMask = 0;
		size_t enchantmentMaskPos = data->wpos();
		*data << uint16(enchantmentMask); // enchantmentMask < 0x1000

		for (uint32 j = 0; j < MAX_ENCHANTMENT_SLOT; ++j) {
			uint32 enchId = item->GetEnchantmentId(EnchantmentSlot(j));

			if (!enchId)
				continue;

			enchantmentMask |= (1 << j);

			*data << uint16(enchId); // enchantmentId?
		}

		data->put<uint16>(enchantmentMaskPos, enchantmentMask);

		*data << uint16(0); // unknown
		data->appendPackGUID(item->GetUInt64Value(ITEM_FIELD_CREATOR)); // item creator
		*data << uint32(0); // seed?
	}

	data->put<uint32>(slotUsedMaskPos, slotUsedMask);
}

void Player::SendEquipmentSetList() {
	uint32 count = 0;
	WorldPacket data(SMSG_EQUIPMENT_SET_LIST, 4);
	size_t count_pos = data.wpos();
	data << uint32(count); // count placeholder
	for (EquipmentSets::iterator itr = m_EquipmentSets.begin();
			itr != m_EquipmentSets.end(); ++itr) {
		if (itr->second.state == EQUIPMENT_SET_DELETED)
			continue;
		data.appendPackGUID(itr->second.Guid);
		data << uint32(itr->first);
		data << itr->second.Name;
		data << itr->second.IconName;
		for (uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
			data.appendPackGUID(
					MAKE_NEW_GUID(itr->second.Items[i], 0, HIGHGUID_ITEM));

		++count; // client have limit but it checked at loading and set
	}
	data.put<uint32>(count_pos, count);
	GetSession()->SendPacket(&data);
}

void Player::SetEquipmentSet(uint32 index, EquipmentSet eqset) {
	if (eqset.Guid != 0) {
		bool found = false;

		for (EquipmentSets::iterator itr = m_EquipmentSets.begin();
				itr != m_EquipmentSets.end(); ++itr) {
			if ((itr->second.Guid == eqset.Guid) && (itr->first == index)) {
				found = true;
				break;
			}
		}

		if (!found) // something wrong...
		{
			sLog->outError(
					"Player %s tried to save equipment set "UI64FMTD" (index %u), but that equipment set not found!",
					GetName(), eqset.Guid, index);
			return;
		}
	}

	EquipmentSet& eqslot = m_EquipmentSets[index];

	EquipmentSetUpdateState old_state = eqslot.state;

	eqslot = eqset;

	if (eqset.Guid == 0) {
		eqslot.Guid = sObjectMgr->GenerateEquipmentSetGuid();

		WorldPacket data(SMSG_EQUIPMENT_SET_SAVED, 4 + 1);
		data << uint32(index);
		data.appendPackGUID(eqslot.Guid);
		GetSession()->SendPacket(&data);
	}

	eqslot.state =
			old_state == EQUIPMENT_SET_NEW ?
					EQUIPMENT_SET_NEW : EQUIPMENT_SET_CHANGED;
}

void Player::_SaveEquipmentSets(SQLTransaction& trans) {
	for (EquipmentSets::iterator itr = m_EquipmentSets.begin();
			itr != m_EquipmentSets.end();) {
		uint32 index = itr->first;
		EquipmentSet& eqset = itr->second;
		PreparedStatement* stmt = NULL;
		uint8 j = 0;
		switch (eqset.state) {
		case EQUIPMENT_SET_UNCHANGED:
			++itr;
			break; // nothing do
		case EQUIPMENT_SET_CHANGED:
			stmt = CharacterDatabase.GetPreparedStatement(CHAR_SET_EQUIP_SET);
			stmt->setString(j++, eqset.Name.c_str());
			stmt->setString(j++, eqset.IconName.c_str());
			for (uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i)
				stmt->setUInt32(j++, eqset.Items[i]);
			stmt->setUInt32(j++, GetGUIDLow());
			stmt->setUInt64(j++, eqset.Guid);
			stmt->setUInt32(j, index);
			trans->Append(stmt);
			eqset.state = EQUIPMENT_SET_UNCHANGED;
			++itr;
			break;
		case EQUIPMENT_SET_NEW:
			stmt = CharacterDatabase.GetPreparedStatement(CHAR_ADD_EQUIP_SET);
			stmt->setUInt32(j++, GetGUIDLow());
			stmt->setUInt64(j++, eqset.Guid);
			stmt->setUInt32(j++, index);
			stmt->setString(j++, eqset.Name.c_str());
			stmt->setString(j++, eqset.IconName.c_str());
			for (uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i)
				stmt->setUInt32(j++, eqset.Items[i]);
			trans->Append(stmt);
			eqset.state = EQUIPMENT_SET_UNCHANGED;
			++itr;
			break;
		case EQUIPMENT_SET_DELETED:
			stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EQUIP_SET);
			stmt->setUInt64(0, eqset.Guid);
			trans->Append(stmt);
			m_EquipmentSets.erase(itr++);
			break;
		}
	}
}

void Player::_SaveBGData(SQLTransaction& trans) {
	PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(
			CHAR_DEL_PLAYER_BGDATA);
	stmt->setUInt32(0, GetGUIDLow());
	trans->Append(stmt);
	if (m_bgData.bgInstanceID) {
		/* guid, bgInstanceID, bgTeam, x, y, z, o, map, taxi[0], taxi[1], mountSpell */
		stmt = CharacterDatabase.GetPreparedStatement(CHAR_ADD_PLAYER_BGDATA);
		stmt->setUInt32(0, GetGUIDLow());
		stmt->setUInt32(1, m_bgData.bgInstanceID);
		stmt->setUInt16(2, m_bgData.bgTeam);
		stmt->setFloat(3, m_bgData.joinPos.GetPositionX());
		stmt->setFloat(4, m_bgData.joinPos.GetPositionY());
		stmt->setFloat(5, m_bgData.joinPos.GetPositionZ());
		stmt->setFloat(6, m_bgData.joinPos.GetOrientation());
		stmt->setUInt16(7, m_bgData.joinPos.GetMapId());
		stmt->setUInt16(8, m_bgData.taxiPath[0]);
		stmt->setUInt16(9, m_bgData.taxiPath[1]);
		stmt->setUInt16(10, m_bgData.mountSpell);
		trans->Append(stmt);
	}
}

void Player::DeleteEquipmentSet(uint64 setGuid) {
	for (EquipmentSets::iterator itr = m_EquipmentSets.begin();
			itr != m_EquipmentSets.end(); ++itr) {
		if (itr->second.Guid == setGuid) {
			if (itr->second.state == EQUIPMENT_SET_NEW)
				m_EquipmentSets.erase(itr);
			else
				itr->second.state = EQUIPMENT_SET_DELETED;
			break;
		}
	}
}

void Player::SetEmoteState(uint32 anim_id) {
	HandleEmoteCommand(anim_id); // Fall back

	m_emote = anim_id;
}

void Player::RemoveAtLoginFlag(AtLoginFlags f, bool in_db_also /*= false*/) {
	m_atLoginFlags &= ~f;

	if (in_db_also)
		CharacterDatabase.PExecute(
				"UPDATE characters set at_login = at_login & ~ %u WHERE guid ='%u'",
				uint32(f), GetGUIDLow());
}

void Player::SendClearCooldown(uint32 spell_id, Unit* target) {
	WorldPacket data(SMSG_CLEAR_COOLDOWN, 4 + 8);
	data << uint32(spell_id);
	data << uint64(target->GetGUID());
	SendDirectMessage(&data);
}

void Player::ResetMap() {
	// this may be called during Map::Update
	// after decrement+unlink, ++m_mapRefIter will continue correctly
	// when the first element of the list is being removed
	// nocheck_prev will return the padding element of the RefManager
	// instead of NULL in the case of prev
	GetMap()->UpdateIteratorBack(this);
	Unit::ResetMap();
	GetMapRef().unlink();
}

void Player::SetMap(Map * map) {
	Unit::SetMap(map);
	m_mapRef.link(map, this);
}

void Player::_LoadGlyphs(PreparedQueryResult result) {
	// SELECT spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6, glyph7, glyph8, glyph9 from character_glyphs WHERE guid = '%u'
	if (!result)
		return;

	do {
		Field* fields = result->Fetch();

		uint8 spec = fields[0].GetUInt8();
		if (spec >= m_specsCount)
			continue;

		m_Glyphs[spec][0] = fields[1].GetUInt16();
		m_Glyphs[spec][1] = fields[2].GetUInt16();
		m_Glyphs[spec][2] = fields[3].GetUInt16();
		m_Glyphs[spec][3] = fields[4].GetUInt16();
		m_Glyphs[spec][4] = fields[5].GetUInt16();
		m_Glyphs[spec][5] = fields[6].GetUInt16();
		m_Glyphs[spec][6] = fields[7].GetUInt16();
		m_Glyphs[spec][7] = fields[8].GetUInt16();
		m_Glyphs[spec][8] = fields[9].GetUInt16();
	} while (result->NextRow());
}

void Player::_SaveGlyphs(SQLTransaction& trans) {
	trans->PAppend("DELETE FROM character_glyphs WHERE guid='%u'",
			GetGUIDLow());
	for (uint8 spec = 0; spec < m_specsCount; ++spec) {
		trans->PAppend(
				"INSERT INTO character_glyphs VALUES('%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')",
				GetGUIDLow(), spec, m_Glyphs[spec][0], m_Glyphs[spec][1],
				m_Glyphs[spec][2], m_Glyphs[spec][3], m_Glyphs[spec][4],
				m_Glyphs[spec][5], m_Glyphs[spec][6], m_Glyphs[spec][7],
				m_Glyphs[spec][8]);
	}
}

void Player::_SaveTalentBranchSpecs(SQLTransaction& trans) {
	trans->PAppend("DELETE FROM character_branchspec WHERE guid='%u'",
			GetGUIDLow());
	for (uint8 spec = 0; spec < m_specsCount; ++spec) {
		trans->PAppend(
				"INSERT INTO character_branchspec VALUES('%u', '%u', '%u')",
				GetGUIDLow(), spec, GetTalentBranchSpec(spec));
	}
}

void Player::_LoadTalentBranchSpecs(PreparedQueryResult result) {
	// SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTBRANCHSPECS, "SELECT branchSpec, spec FROM character_branchspec WHERE guid = '%u'", GUID_LOPART(m_guid));
	if (result) {
		do {
			Field* fields = result->Fetch();

			uint32 branchSpec = fields[0].GetUInt32();
			uint8 spec = fields[1].GetUInt32();
			SetTalentBranchSpec(branchSpec, spec);
		} while (result->NextRow());
	}
}

void Player::_LoadTalents(PreparedQueryResult result) {
	// SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTS, "SELECT spell, spec FROM character_talent WHERE guid = '%u'", GUID_LOPART(m_guid));
	if (result) {
		do
			AddTalent((*result)[0].GetUInt32(), (*result)[1].GetUInt32(),
					false);
		while (result->NextRow());
	}
}

void Player::_SaveTalents(SQLTransaction& trans) {
	for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i) {
		for (PlayerTalentMap::iterator itr = m_talents[i]->begin();
				itr != m_talents[i]->end();) {
			if (itr->second->state == PLAYERSPELL_REMOVED
					|| itr->second->state == PLAYERSPELL_CHANGED)
				trans->PAppend(
						"DELETE FROM character_talent WHERE guid = '%u' and spell = '%u' and spec = '%u'",
						GetGUIDLow(), itr->first, itr->second->spec);

			if (itr->second->state == PLAYERSPELL_NEW
					|| itr->second->state == PLAYERSPELL_CHANGED)
				trans->PAppend(
						"INSERT INTO character_talent (guid, spell, spec) VALUES ('%u', '%u', '%u')",
						GetGUIDLow(), itr->first, itr->second->spec);

			if (itr->second->state == PLAYERSPELL_REMOVED) {
				delete itr->second;
				m_talents[i]->erase(itr++);
			} else {
				itr->second->state = PLAYERSPELL_UNCHANGED;
				++itr;
			}
		}
	}
}

void Player::UpdateSpecCount(uint8 count) {
	uint32 curCount = GetSpecsCount();
	if (curCount == count)
		return;

	if (m_activeSpec >= count)
		ActivateSpec(0);

	SQLTransaction trans = CharacterDatabase.BeginTransaction();

	// Copy spec data
	if (count > curCount) {
		_SaveActions(trans); // make sure the button list is cleaned up
		for (ActionButtonList::iterator itr = m_actionButtons.begin();
				itr != m_actionButtons.end(); ++itr)
			trans->PAppend(
					"INSERT INTO character_action (guid, button, action, type, spec) VALUES ('%u', '%u', '%u', '%u', '%u')",
					GetGUIDLow(), uint32(itr->first),
					uint32(itr->second.GetAction()),
					uint32(itr->second.GetType()), 1);
	}
	// Delete spec data for removed spec.
	else if (count < curCount) {
		_SaveActions(trans);
		trans->PAppend(
				"DELETE FROM character_action WHERE spec<>'%u' AND guid='%u'",
				m_activeSpec, GetGUIDLow());
		m_activeSpec = 0;
	}

	CharacterDatabase.CommitTransaction(trans);

	SetSpecsCount(count);

	SendTalentsInfoData(false);
}

void Player::ActivateSpec(uint8 spec) {
	if (GetActiveSpec() == spec)
		return;

	if (spec > GetSpecsCount())
		return;

	if (IsNonMeleeSpellCasted(false))
		InterruptNonMeleeSpells(false);

	SQLTransaction trans = CharacterDatabase.BeginTransaction();
	_SaveActions(trans);
	CharacterDatabase.CommitTransaction(trans);

	UnsummonPetTemporaryIfAny();

	// TO-DO: We need more research to know what happens with warlock's reagent
	if (Pet* pet = GetPet())
		RemovePet(pet, PET_SLOT_ACTUAL_PET_SLOT, true);

	ClearComboPointHolders();
	ClearAllReactives();
	UnsummonAllTotems();
	RemoveAllControlled();
	/*RemoveAllAurasOnDeath();
	 if (GetPet())
	 GetPet()->RemoveAllAurasOnDeath();*/

	//RemoveAllAuras(GetGUID(), NULL, false, true); // removes too many auras
	//ExitVehicle(); // should be impossible to switch specs from inside a vehicle..
	// Let client clear his current Actions
	SendActionButtons(2);
	// m_actionButtons.clear() is called in the next _LoadActionButtons
	for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i) {
		TalentEntry const *talentInfo = sTalentStore.LookupEntry(i);

		if (!talentInfo)
			continue;

		TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(
				talentInfo->TalentTab);

		if (!talentTabInfo)
			continue;

		// unlearn only talents for character class
		// some spell learned by one class as normal spells or know at creation but another class learn it as talent,
		// to prevent unexpected lost normal learned spell skip another class talents
		if ((getClassMask() & talentTabInfo->ClassMask) == 0)
			continue;

		for (int8 rank = MAX_TALENT_RANK - 1; rank >= 0; --rank) {
			// skip non-existant talent ranks
			if (talentInfo->RankID[rank] == 0)
				continue;
			removeSpell(talentInfo->RankID[rank], true); // removes the talent, and all dependant, learned, and chained spells..
			if (const SpellEntry *_spellEntry = sSpellStore.LookupEntry(talentInfo->RankID[rank]))
				for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) // search through the SpellEntry for valid trigger spells
					if (_spellEntry->EffectTriggerSpell[i] > 0
							&& _spellEntry->Effect[i]
									== SPELL_EFFECT_LEARN_SPELL)
						removeSpell(_spellEntry->EffectTriggerSpell[i], true); // and remove any spells that the talent teaches
			// if this talent rank can be found in the PlayerTalentMap, mark the talent as removed so it gets deleted
			//PlayerTalentMap::iterator plrTalent = m_talents[m_activeSpec]->find(talentInfo->RankID[rank]);
			//if (plrTalent != m_talents[m_activeSpec]->end())
			//    plrTalent->second->state = PLAYERSPELL_REMOVED;
		}
	}

	for (uint32 i = 0; i < sTalentTreePrimarySpellsStore.GetNumRows(); ++i) {
		TalentTreePrimarySpellsEntry const *talentInfo =
				sTalentTreePrimarySpellsStore.LookupEntry(i);

		if (!talentInfo
				|| talentInfo->TalentTabID != GetTalentBranchSpec(m_activeSpec))
			continue;

		removeSpell(talentInfo->SpellID, true);
	}

	// set glyphs
	for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
		// remove secondary glyph
		if (uint32 oldglyph = m_Glyphs[m_activeSpec][slot])
			if (GlyphPropertiesEntry const *old_gp = sGlyphPropertiesStore.LookupEntry(oldglyph))
				RemoveAurasDueToSpell(old_gp->SpellId);

	SetActiveSpec(spec);
	uint32 spentTalents = 0;

	for (uint32 talentId = 0; talentId < sTalentStore.GetNumRows();
			++talentId) {
		TalentEntry const *talentInfo = sTalentStore.LookupEntry(talentId);

		if (!talentInfo)
			continue;

		TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(
				talentInfo->TalentTab);

		if (!talentTabInfo)
			continue;

		// learn only talents for character class
		if ((getClassMask() & talentTabInfo->ClassMask) == 0)
			continue;

		// learn highest talent rank that exists in newly activated spec
		for (int8 rank = MAX_TALENT_RANK - 1; rank >= 0; --rank) {
			// skip non-existant talent ranks
			if (talentInfo->RankID[rank] == 0)
				continue;
			// if the talent can be found in the newly activated PlayerTalentMap
			if (HasTalent(talentInfo->RankID[rank], m_activeSpec)) {
				learnSpell(talentInfo->RankID[rank], false); // add the talent to the PlayerSpellMap
				spentTalents += (rank + 1); // increment the spentTalents count
			}
		}
	}

	for (uint32 i = 0; i < sTalentTreePrimarySpellsStore.GetNumRows(); ++i) {
		TalentTreePrimarySpellsEntry const *talentInfo =
				sTalentTreePrimarySpellsStore.LookupEntry(i);

		if (!talentInfo || talentInfo->TalentTabID != GetTalentBranchSpec(spec))
			continue;

		learnSpell(talentInfo->SpellID, false);
	}

	// set glyphs
	for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot) {
		uint32 glyph = m_Glyphs[m_activeSpec][slot];

		// apply primary glyph
		if (glyph)
			if (GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyph))
				CastSpell(this, gp->SpellId, true);

		SetGlyph(slot, glyph);
	}

	m_usedTalentCount = spentTalents;
	InitTalentForLevel();

	{
		PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(
				CHAR_LOAD_PLAYER_ACTIONS_SPEC);
		stmt->setUInt32(0, GetGUIDLow());
		stmt->setUInt8(1, m_activeSpec);
		if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
			_LoadActions(result);
	}

	SendActionButtons(1);

	Powers pw = getPowerType();
	if (pw != POWER_MANA)
		SetPower(POWER_MANA, 0); // Mana must be 0 even if it isn't the active power type.

	SetPower(pw, 0);
}

void Player::ResetTimeSync() {
	m_timeSyncCounter = 0;
	m_timeSyncTimer = 0;
	m_timeSyncClient = 0;
	m_timeSyncServer = getMSTime();
}

void Player::SendTimeSync() {
	WorldPacket data(SMSG_TIME_SYNC_REQ, 4);
	data << uint32(m_timeSyncCounter++);
	GetSession()->SendPacket(&data);

	// Schedule next sync in 10 sec
	m_timeSyncTimer = 10000;
	m_timeSyncServer = getMSTime();
}

void Player::SetReputation(uint32 factionentry, uint32 value) {
	GetReputationMgr().SetReputation(sFactionStore.LookupEntry(factionentry),
			value);
}
uint32 Player::GetReputation(uint32 factionentry) {
	return GetReputationMgr().GetReputation(
			sFactionStore.LookupEntry(factionentry));
}
std::string Player::GetGuildName() {
	return sObjectMgr->GetGuildById(GetGuildId())->GetName();
}

void Player::SendDuelCountdown(uint32 counter) {
	WorldPacket data(SMSG_DUEL_COUNTDOWN, 4);
	data << uint32(counter); // seconds
	GetSession()->SendPacket(&data);
}

void Player::AddRefundReference(uint32 it) {
	m_refundableItems.insert(it);
}

void Player::DeleteRefundReference(uint32 it) {
	std::set<uint32>::iterator itr = m_refundableItems.find(it);
	if (itr != m_refundableItems.end()) {
		m_refundableItems.erase(itr);
	}
}

void Player::SendRefundInfo(Item *item) {
	// This function call unsets ITEM_FLAGS_REFUNDABLE if played time is over 2 hours.
	item->UpdatePlayedTime(this);

	if (!item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE)) {
		sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
				"Item refund: item not refundable!");
		return;
	}

	if (GetGUIDLow() != item->GetRefundRecipient()) // Formerly refundable item got traded
			{
		sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
				"Item refund: item was traded!");
		item->SetNotRefundable(this);
		return;
	}

	ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(
			item->GetPaidExtendedCost());
	if (!iece) {
		sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
				"Item refund: cannot find extendedcost data.");
		return;
	}

	WorldPacket data(SMSG_ITEM_REFUND_INFO_RESPONSE,
			8 + 4 + 4 + 4 + 4 * 4 + 4 * 4 + 4 + 4);
	data << uint64(item->GetGUID()); // item guid
	data << uint32(item->GetPaidMoney()); // money cost
	//data << uint32(iece->reqhonorpoints);               // honor point cost
	//data << uint32(iece->reqarenapoints);               // arena point cost
	for (uint8 i = 0; i < MAX_ITEM_EXTENDED_COST_REQUIREMENTS; ++i) // item cost data
			{
		data << uint32(iece->RequiredCurrency[i]);
		data << uint32(iece->RequiredCurrencyCount[i]);
	}
	data << uint32(0);
	data << uint32(GetTotalPlayedTime() - item->GetPlayedTime());
	GetSession()->SendPacket(&data);
}

bool Player::AddItem(uint32 itemId, uint32 count) {
	uint32 noSpaceForCount = 0;
	ItemPosCountVec dest;
	uint8 msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, count,
			&noSpaceForCount);
	if (msg != EQUIP_ERR_OK)
		count = noSpaceForCount;

	if (count == 0 || dest.empty()) {
		// -- TODO: Send to mailbox if no space
		ChatHandler(this).PSendSysMessage(
				"You don't have any space in your bags.");
		return false;
	}

	Item* item = StoreNewItem(dest, itemId, true,
			Item::GenerateItemRandomPropertyId(itemId));
	if (item)
		SendNewItem(item, count, true, false);
	else
		return false;
	return true;
}

void Player::RefundItem(Item *item) {
	if (!item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE)) {
		sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
				"Item refund: item not refundable!");
		return;
	}

	if (item->IsRefundExpired()) // item refund has expired
	{
		item->SetNotRefundable(this);
		WorldPacket data(SMSG_ITEM_REFUND_RESULT, 8 + 4);
		data << uint64(item->GetGUID()); // Guid
		data << uint32(10); // Error!
		GetSession()->SendPacket(&data);
		return;
	}

	if (GetGUIDLow() != item->GetRefundRecipient()) // Formerly refundable item got traded
			{
		sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
				"Item refund: item was traded!");
		item->SetNotRefundable(this);
		return;
	}

	ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(
			item->GetPaidExtendedCost());
	if (!iece) {
		sLog->outDebug(LOG_FILTER_PLAYER_ITEMS,
				"Item refund: cannot find extendedcost data.");
		return;
	}

	bool store_error = false;
	for (uint8 i = 0; i < MAX_EXTENDED_COST_CURRENCIES; ++i) {
		uint32 count = iece->RequiredCurrency[i];
		uint32 itemid = iece->RequiredCurrencyCount[i];

		if (count && itemid) {
			ItemPosCountVec dest;
			uint8 msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemid,
					count);
			if (msg != EQUIP_ERR_OK) {
				store_error = true;
				break;
			}
		}
	}

	if (store_error) {
		WorldPacket data(SMSG_ITEM_REFUND_RESULT, 8 + 4);
		data << uint64(item->GetGUID()); // Guid
		data << uint32(10); // Error!
		GetSession()->SendPacket(&data);
		return;
	}

	WorldPacket data(SMSG_ITEM_REFUND_RESULT,
			8 + 4 + 4 + 4 + 4 + 4 * 4 + 4 * 4);
	data << uint64(item->GetGUID()); // item guid
	data << uint32(0); // 0, or error code
	data << uint32(item->GetPaidMoney()); // money cost
	//data << uint32(iece->reqhonorpoints);               // honor point cost
	//data << uint32(iece->reqarenapoints);               // arena point cost
	for (uint8 i = 0; i < MAX_EXTENDED_COST_CURRENCIES; ++i) // item cost data
			{
		data << uint32(iece->RequiredCurrency[i]);
		data << uint32(iece->RequiredCurrencyCount[i]);
	}
	GetSession()->SendPacket(&data);

	uint32 moneyRefund = item->GetPaidMoney(); // item-> will be invalidated in DestroyItem

	// Delete any references to the refund data
	item->SetNotRefundable(this);

	// Destroy item
	DestroyItem(item->GetBagSlot(), item->GetSlot(), true);

	// Grant back extendedcost items
	for (uint8 i = 0; i < MAX_EXTENDED_COST_CURRENCIES; ++i) {
		uint32 count = iece->RequiredCurrencyCount[i];
		uint32 itemid = iece->RequiredCurrency[i];
		if (count && itemid) {
			ItemPosCountVec dest;
			uint8 msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemid,
					count);
			ASSERT(msg == EQUIP_ERR_OK) /// Already checked before
			Item* it = StoreNewItem(dest, itemid, true);
			SendNewItem(it, count, true, false, true);
		}
	}

	// Grant back money
	if (moneyRefund)
		ModifyMoney(moneyRefund);
}

void Player::SetRandomWinner(bool isWinner) {
	m_IsBGRandomWinner = isWinner;
	if (m_IsBGRandomWinner)
		CharacterDatabase.PExecute(
				"INSERT INTO character_battleground_random (guid) VALUES ('%u')",
				GetGUIDLow());
}

void Player::_LoadRandomBGStatus(PreparedQueryResult result) {
	//QueryResult result = CharacterDatabase.PQuery("SELECT guid FROM character_battleground_random WHERE guid = '%u'", GetGUIDLow());

	if (result)
		m_IsBGRandomWinner = true;
}

float Player::GetAverageItemLevel() {
	float sum = 0;
	uint32 count = 0;

	for (int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) {
		// don't check tabard, ranged, offhand or chest
		if (i == EQUIPMENT_SLOT_TABARD || i == EQUIPMENT_SLOT_RANGED
				|| i == EQUIPMENT_SLOT_OFFHAND || i == EQUIPMENT_SLOT_BODY)
			continue;

		if (m_items[i] && m_items[i]->GetProto())
			sum += m_items[i]->GetProto()->GetItemLevelIncludingQuality();

		count++;
	}

	return ((float) sum) / count;
}

void Player::_LoadInstanceTimeRestrictions(PreparedQueryResult result) {
	if (!result)
		return;

	do {
		Field* fields = result->Fetch();
		_instanceResetTimes.insert(
				InstanceTimeMap::value_type(fields[0].GetUInt32(),
						fields[1].GetUInt64()));
	} while (result->NextRow());
}

void Player::_SaveInstanceTimeRestrictions(SQLTransaction& trans) {
	if (_instanceResetTimes.empty())
		return;

	PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(
			CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES);
	stmt->setUInt32(0, GetSession()->GetAccountId());
	trans->Append(stmt);

	for (InstanceTimeMap::const_iterator itr = _instanceResetTimes.begin();
			itr != _instanceResetTimes.end(); ++itr) {
		stmt = CharacterDatabase.GetPreparedStatement(
				CHAR_ADD_ACCOUNT_INSTANCE_LOCK_TIMES);
		stmt->setUInt32(0, GetSession()->GetAccountId());
		stmt->setUInt32(1, itr->first);
		stmt->setUInt64(2, itr->second);
		trans->Append(stmt);
	}
}

void Player::SendClearFocus(Unit* target) {
	WorldPacket data(SMSG_BREAK_TARGET, target->GetPackGUID().size());
	data.append(target->GetPackGUID());
	GetSession()->SendPacket(&data);
}

void Player::SetInGuild(uint32 GuildId) {
	//printf("DEBUG: set in guild %u, %lX", GuildId, MAKE_NEW_GUID(GuildId, 0, HIGHGUID_GUILD));
	m_guildId = GuildId;
	if (GuildId != 0) {
		SetUInt64Value(OBJECT_FIELD_DATA,
				MAKE_NEW_GUID(GuildId, 0, HIGHGUID_GUILD));
		SetUInt32Value(OBJECT_FIELD_TYPE,
				GetUInt32Value(OBJECT_FIELD_TYPE) | TYPEMASK_IN_GUILD);
	} else {
		SetUInt64Value(OBJECT_FIELD_DATA, 0);
		SetUInt32Value(OBJECT_FIELD_TYPE,
				GetUInt32Value(OBJECT_FIELD_TYPE) & ~TYPEMASK_IN_GUILD);
	}
}

void Player::BroadcastMessage(const char* Format, ...) {
	va_list l;
	va_start(l, Format);
	char Message[1024];
	vsnprintf(Message, 1024, Format, l);
	va_end(l);

	WorldPacket data;
	ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_SYSTEM, LANG_UNIVERSAL,
			NULL, 0, Message, NULL);
	GetSession()->SendPacket(&data);
}

void Player::SendToManyPets(Player *pl) {
	ChatHandler(pl).PSendSysMessage(LANG_FAILED_NO_PLACE_FOR_PET);
}

void Player::RemoveOrAddMasterySpells() {
	if (!isAlive())
		return;

	if (!HasAuraType(SPELL_AURA_MASTERY)
			|| GetTalentBranchSpec(GetActiveSpec()) == 0) {
		if (HasAura(77514))
			RemoveAurasDueToSpell(77514);

		if (HasAura(77515))
			RemoveAurasDueToSpell(77515);

		if (HasAura(77493))
			RemoveAurasDueToSpell(77493);

		if (HasAura(76658))
			RemoveAurasDueToSpell(76658);

		if (HasAura(76657))
			RemoveAurasDueToSpell(76657);

		if (HasAura(76595))
			RemoveAurasDueToSpell(76595);

		if (HasAura(76671))
			RemoveAurasDueToSpell(76671);

		if (HasAura(77220))
			RemoveAurasDueToSpell(77220);

		if (HasAura(76857))
			RemoveAurasDueToSpell(76857);
	} else if (HasAuraType(SPELL_AURA_MASTERY)) {
		if (GetTalentBranchSpec(GetActiveSpec()) == BS_DEATH_KNIGHT_FROST)
			if (!HasAura(77514))
				AddAura(77514, this);

		if (GetTalentBranchSpec(GetActiveSpec()) == BS_DEATH_KNIGHT_UNHOLY)
			if (!HasAura(77515))
				AddAura(77515, this);

		if (GetTalentBranchSpec(GetActiveSpec()) == BS_DRUID_FERAL_COMBAT)
			if (!HasAura(77493))
				AddAura(77493, this);

		if (GetTalentBranchSpec(GetActiveSpec()) == BS_HUNTER_SURVIVAL)
			if (!HasAura(76658))
				AddAura(76658, this);

		if (GetTalentBranchSpec(GetActiveSpec()) == BS_HUNTER_BEAST_MASTERY)
			if (!HasAura(76657))
				AddAura(76657, this);

		if (GetTalentBranchSpec(GetActiveSpec()) == BS_MAGE_FIRE)
			if (!HasAura(76595))
				AddAura(76595, this);

		if (GetTalentBranchSpec(GetActiveSpec()) == BS_PALADIN_PROTECTION)
			if (!HasAura(76671))
				AddAura(76671, this);

		if (GetTalentBranchSpec(GetActiveSpec()) == BS_WARLOCK_DESTRUCTION)
			if (!HasAura(77220))
				AddAura(77220, this);

		if (GetTalentBranchSpec(GetActiveSpec()) == BS_WARRIOR_PROTECTION)
			if (!HasAura(76857))
				AddAura(76857, this);
	}
}
